Changeset 1169 in josm for trunk/src/org


Ignore:
Timestamp:
2008-12-23T15:07:05+01:00 (16 years ago)
Author:
stoecker
Message:

removed usage of tab stops

Location:
trunk/src/org/openstreetmap/josm
Files:
256 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/Main.java

    r1163 r1169  
    182182
    183183    /**
    184      * Remove the specified layer from the map. If it is the last layer, 
     184     * Remove the specified layer from the map. If it is the last layer,
    185185     * remove the map as well.
    186186     */
     
    219219
    220220    /**
    221      * Load all plugins specified in preferences. If the parameter is 
     221     * Load all plugins specified in preferences. If the parameter is
    222222     * <code>true</code>, all early plugins are loaded (before constructor).
    223223     */
     
    281281                Main.pref.put("pluginmanager.lastupdate",Long.toString(tim));
    282282            } else if (d > maxTime) {
    283                 JOptionPane.showMessageDialog(Main.parent, 
    284                    "<html>" + 
     283                JOptionPane.showMessageDialog(Main.parent,
     284                   "<html>" +
    285285                   tr("Last plugin update more than {0} days ago.", d) +
    286                    "<br><em>" + 
     286                   "<br><em>" +
    287287                   tr("(You can change the number of days after which this warning appears<br>by setting the config option 'pluginmanager.warntime'.)") +
    288288                   "</html>");
     
    512512        } else if (os.toLowerCase().startsWith("windows")) {
    513513            platform = new PlatformHookWindows();
    514         } else if (os.equals("Linux") || os.equals("Solaris") || 
    515             os.equals("SunOS") || os.equals("AIX") || 
     514        } else if (os.equals("Linux") || os.equals("Solaris") ||
     515            os.equals("SunOS") || os.equals("AIX") ||
    516516            os.equals("FreeBSD") || os.equals("NetBSD") || os.equals("OpenBSD")) {
    517517            platform = new PlatformHookUnixoid();
  • trunk/src/org/openstreetmap/josm/actions/AboutAction.java

    r1138 r1169  
    5151public class AboutAction extends JosmAction {
    5252
    53         private static final String version;
    54 
    55         private final static JTextArea revision;
    56         private static String time;
     53    private static final String version;
     54
     55    private final static JTextArea revision;
     56    private static String time;
    5757
    5858    static {
     
    6060        if(u == null) {
    6161            try {
    62                 u = new URL("jar:" + Main.class.getProtectionDomain().getCodeSource().getLocation().toString() 
     62                u = new URL("jar:" + Main.class.getProtectionDomain().getCodeSource().getLocation().toString()
    6363                        + "!/META-INF/MANIFEST.MF");
    6464            } catch (MalformedURLException e) {
     
    6666            }
    6767        }
    68                 revision = loadFile(u);
    69 
    70                 Pattern versionPattern = Pattern.compile(".*?(?:Revision|Main-Version): ([0-9]*(?: SVN)?).*", Pattern.CASE_INSENSITIVE|Pattern.DOTALL);
    71                 Matcher match = versionPattern.matcher(revision.getText());
    72                 version = match.matches() ? match.group(1) : tr("UNKNOWN");
    73 
    74                 Pattern timePattern = Pattern.compile(".*?(?:Last Changed Date|Main-Date): ([^\n]*).*", Pattern.CASE_INSENSITIVE|Pattern.DOTALL);
    75                 match = timePattern.matcher(revision.getText());
    76                 time = match.matches() ? match.group(1) : tr("UNKNOWN");
    77         }
    78 
    79         /**
    80         * Return string describing version.
    81         * Note that the strinc contains the version number plus an optional suffix of " SVN" to indicate an unofficial development build.
    82         * @return version string
    83         */
    84         static public String getVersionString() {
    85                 return version;
    86         }
     68        revision = loadFile(u);
     69
     70        Pattern versionPattern = Pattern.compile(".*?(?:Revision|Main-Version): ([0-9]*(?: SVN)?).*", Pattern.CASE_INSENSITIVE|Pattern.DOTALL);
     71        Matcher match = versionPattern.matcher(revision.getText());
     72        version = match.matches() ? match.group(1) : tr("UNKNOWN");
     73
     74        Pattern timePattern = Pattern.compile(".*?(?:Last Changed Date|Main-Date): ([^\n]*).*", Pattern.CASE_INSENSITIVE|Pattern.DOTALL);
     75        match = timePattern.matcher(revision.getText());
     76        time = match.matches() ? match.group(1) : tr("UNKNOWN");
     77    }
     78
     79    /**
     80    * Return string describing version.
     81    * Note that the strinc contains the version number plus an optional suffix of " SVN" to indicate an unofficial development build.
     82    * @return version string
     83    */
     84    static public String getVersionString() {
     85        return version;
     86    }
    8787
    8888    /**
     
    9999        return myVersion;
    100100    }
    101        
     101
    102102    /**
    103103     * check whether the version is a development build out of SVN.
     
    107107        return version.endsWith(" SVN");
    108108    }
    109        
    110         public AboutAction() {
    111                 super(tr("About"), "about", tr("Display the about screen."), Shortcut.registerShortcut("system:about", tr("About"), KeyEvent.VK_F1, Shortcut.GROUP_DIRECT, Shortcut.SHIFT_DEFAULT), true);
    112         }
    113 
    114         public void actionPerformed(ActionEvent e) {
    115                 JTabbedPane about = new JTabbedPane();
    116 
    117                 JTextArea readme = loadFile(Main.class.getResource("/README"));
    118                 JTextArea contribution = loadFile(Main.class.getResource("/CONTRIBUTION"));
    119 
    120                 JPanel info = new JPanel(new GridBagLayout());
    121                 info.add(new JLabel(tr("Java OpenStreetMap Editor Version {0}",version)), GBC.eol().fill(GBC.HORIZONTAL));
    122                 info.add(new JLabel(tr("last change at {0}",time)), GBC.eol().fill(GBC.HORIZONTAL));
    123                 info.add(new JLabel(tr("Java Version {0}",System.getProperty("java.version"))), GBC.eol().fill(GBC.HORIZONTAL));
    124                 info.add(GBC.glue(0,10), GBC.eol());
    125                 info.add(new JLabel(tr("Homepage")), GBC.std().insets(0,0,10,0));
    126                 info.add(new UrlLabel("http://josm.openstreetmap.de"), GBC.eol().fill(GBC.HORIZONTAL));
    127                 info.add(new JLabel(tr("Bug Reports")), GBC.std().insets(0,0,10,0));
    128                 info.add(new UrlLabel("http://josm.openstreetmap.de/newticket"), GBC.eol().fill(GBC.HORIZONTAL));
    129                 info.add(new JLabel(tr("News about JOSM")), GBC.std().insets(0,0,10,0));
    130                 info.add(new UrlLabel("http://www.opengeodata.org/?cat=17"), GBC.eol().fill(GBC.HORIZONTAL));
    131 
    132                 about.addTab(tr("Info"), info);
    133                 about.addTab(tr("Readme"), createScrollPane(readme));
    134                 about.addTab(tr("Revision"), createScrollPane(revision));
    135                 about.addTab(tr("Contribution"), createScrollPane(contribution));
    136 
    137                 JPanel pluginTab = new JPanel(new GridBagLayout());
    138                 for (final PluginProxy p : Main.plugins) {
    139                         String name = p.info.name + (p.info.version != null && !p.info.version.equals("") ? " Version: "+p.info.version : "");
    140                         pluginTab.add(new JLabel(name), GBC.std());
    141                         pluginTab.add(Box.createHorizontalGlue(), GBC.std().fill(GBC.HORIZONTAL));
    142                         pluginTab.add(new JButton(new AbstractAction(tr("Information")){
    143                                 public void actionPerformed(ActionEvent event) {
    144                                         StringBuilder b = new StringBuilder();
    145                                         for (Entry<String,String> e : p.info.attr.entrySet()) {
    146                                                 b.append(e.getKey());
    147                                                 b.append(": ");
    148                                                 b.append(e.getValue());
    149                                                 b.append("\n");
    150                                         }
    151                                         JTextArea a = new JTextArea(10,40);
    152                                         a.setEditable(false);
    153                                         a.setText(b.toString());
    154                                         JOptionPane.showMessageDialog(Main.parent, new JScrollPane(a));
    155                                 }
    156                         }), GBC.eol());
    157                         JLabel label = new JLabel("<html><i>"+(p.info.description==null?tr("no description available"):p.info.description)+"</i></html>");
    158                         label.setBorder(BorderFactory.createEmptyBorder(0,20,0,0));
    159                         label.setMaximumSize(new Dimension(450,1000));
    160                         pluginTab.add(label, GBC.eop().fill(GBC.HORIZONTAL));
    161                 }
    162                 about.addTab(tr("Plugins"), new JScrollPane(pluginTab));
    163 
    164                 about.setPreferredSize(new Dimension(500,300));
    165 
    166                 JOptionPane.showMessageDialog(Main.parent, about, tr("About JOSM..."),
    167                                 JOptionPane.INFORMATION_MESSAGE, ImageProvider.get("logo"));
    168         }
    169 
    170         private JScrollPane createScrollPane(JTextArea area) {
    171                 area.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
    172                 area.setOpaque(false);
    173                 JScrollPane sp = new JScrollPane(area);
    174                 sp.setBorder(null);
    175                 sp.setOpaque(false);
    176                 return sp;
    177         }
    178 
    179         /**
    180         * Retrieve the latest JOSM version from the JOSM homepage.
    181         * @return An string with the latest version or "UNKNOWN" in case
    182          *              of problems (e.g. no internet connection).
    183         */
    184         public static String checkLatestVersion() {
    185                 String latest;
    186                 try {
    187                         InputStream s = new URL("http://josm.openstreetmap.de/current").openStream();
    188                         latest = new BufferedReader(new InputStreamReader(s)).readLine();
    189                         s.close();
    190                 } catch (IOException x) {
    191                         x.printStackTrace();
    192                         return tr("UNKNOWN");
    193                 }
    194                 return latest;
    195         }
    196 
    197         /**
    198         * Load the specified resource into an TextArea and return it.
    199         * @param resource The resource url to load
    200          * @return      An read-only text area with the content of "resource"
    201         */
    202         private static JTextArea loadFile(URL resource) {
    203                 JTextArea area = new JTextArea(tr("File could not be found."));
    204                 area.setEditable(false);
    205                 Font font = Font.getFont("monospaced");
    206                 if (font != null)
    207                         area.setFont(font);
    208                 if (resource == null)
    209                         return area;
    210                 BufferedReader in;
    211                 try {
    212                         in = new BufferedReader(new InputStreamReader(resource.openStream()));
    213                         StringBuilder sb = new StringBuilder();
    214                         for (String line = in.readLine(); line != null; line = in.readLine()) {
    215                                 sb.append(line);
    216                                 sb.append('\n');
    217                         }
    218                         area.setText(sb.toString());
    219                         area.setCaretPosition(0);
    220                 } catch (IOException e) {
    221                         e.printStackTrace();
    222                 }
    223                 return area;
    224         }
     109
     110    public AboutAction() {
     111        super(tr("About"), "about", tr("Display the about screen."), Shortcut.registerShortcut("system:about", tr("About"), KeyEvent.VK_F1, Shortcut.GROUP_DIRECT, Shortcut.SHIFT_DEFAULT), true);
     112    }
     113
     114    public void actionPerformed(ActionEvent e) {
     115        JTabbedPane about = new JTabbedPane();
     116
     117        JTextArea readme = loadFile(Main.class.getResource("/README"));
     118        JTextArea contribution = loadFile(Main.class.getResource("/CONTRIBUTION"));
     119
     120        JPanel info = new JPanel(new GridBagLayout());
     121        info.add(new JLabel(tr("Java OpenStreetMap Editor Version {0}",version)), GBC.eol().fill(GBC.HORIZONTAL));
     122        info.add(new JLabel(tr("last change at {0}",time)), GBC.eol().fill(GBC.HORIZONTAL));
     123        info.add(new JLabel(tr("Java Version {0}",System.getProperty("java.version"))), GBC.eol().fill(GBC.HORIZONTAL));
     124        info.add(GBC.glue(0,10), GBC.eol());
     125        info.add(new JLabel(tr("Homepage")), GBC.std().insets(0,0,10,0));
     126        info.add(new UrlLabel("http://josm.openstreetmap.de"), GBC.eol().fill(GBC.HORIZONTAL));
     127        info.add(new JLabel(tr("Bug Reports")), GBC.std().insets(0,0,10,0));
     128        info.add(new UrlLabel("http://josm.openstreetmap.de/newticket"), GBC.eol().fill(GBC.HORIZONTAL));
     129        info.add(new JLabel(tr("News about JOSM")), GBC.std().insets(0,0,10,0));
     130        info.add(new UrlLabel("http://www.opengeodata.org/?cat=17"), GBC.eol().fill(GBC.HORIZONTAL));
     131
     132        about.addTab(tr("Info"), info);
     133        about.addTab(tr("Readme"), createScrollPane(readme));
     134        about.addTab(tr("Revision"), createScrollPane(revision));
     135        about.addTab(tr("Contribution"), createScrollPane(contribution));
     136
     137        JPanel pluginTab = new JPanel(new GridBagLayout());
     138        for (final PluginProxy p : Main.plugins) {
     139            String name = p.info.name + (p.info.version != null && !p.info.version.equals("") ? " Version: "+p.info.version : "");
     140            pluginTab.add(new JLabel(name), GBC.std());
     141            pluginTab.add(Box.createHorizontalGlue(), GBC.std().fill(GBC.HORIZONTAL));
     142            pluginTab.add(new JButton(new AbstractAction(tr("Information")){
     143                public void actionPerformed(ActionEvent event) {
     144                    StringBuilder b = new StringBuilder();
     145                    for (Entry<String,String> e : p.info.attr.entrySet()) {
     146                        b.append(e.getKey());
     147                        b.append(": ");
     148                        b.append(e.getValue());
     149                        b.append("\n");
     150                    }
     151                    JTextArea a = new JTextArea(10,40);
     152                    a.setEditable(false);
     153                    a.setText(b.toString());
     154                    JOptionPane.showMessageDialog(Main.parent, new JScrollPane(a));
     155                }
     156            }), GBC.eol());
     157            JLabel label = new JLabel("<html><i>"+(p.info.description==null?tr("no description available"):p.info.description)+"</i></html>");
     158            label.setBorder(BorderFactory.createEmptyBorder(0,20,0,0));
     159            label.setMaximumSize(new Dimension(450,1000));
     160            pluginTab.add(label, GBC.eop().fill(GBC.HORIZONTAL));
     161        }
     162        about.addTab(tr("Plugins"), new JScrollPane(pluginTab));
     163
     164        about.setPreferredSize(new Dimension(500,300));
     165
     166        JOptionPane.showMessageDialog(Main.parent, about, tr("About JOSM..."),
     167                JOptionPane.INFORMATION_MESSAGE, ImageProvider.get("logo"));
     168    }
     169
     170    private JScrollPane createScrollPane(JTextArea area) {
     171        area.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
     172        area.setOpaque(false);
     173        JScrollPane sp = new JScrollPane(area);
     174        sp.setBorder(null);
     175        sp.setOpaque(false);
     176        return sp;
     177    }
     178
     179    /**
     180    * Retrieve the latest JOSM version from the JOSM homepage.
     181    * @return An string with the latest version or "UNKNOWN" in case
     182     *      of problems (e.g. no internet connection).
     183    */
     184    public static String checkLatestVersion() {
     185        String latest;
     186        try {
     187            InputStream s = new URL("http://josm.openstreetmap.de/current").openStream();
     188            latest = new BufferedReader(new InputStreamReader(s)).readLine();
     189            s.close();
     190        } catch (IOException x) {
     191            x.printStackTrace();
     192            return tr("UNKNOWN");
     193        }
     194        return latest;
     195    }
     196
     197    /**
     198    * Load the specified resource into an TextArea and return it.
     199    * @param resource The resource url to load
     200     * @return  An read-only text area with the content of "resource"
     201    */
     202    private static JTextArea loadFile(URL resource) {
     203        JTextArea area = new JTextArea(tr("File could not be found."));
     204        area.setEditable(false);
     205        Font font = Font.getFont("monospaced");
     206        if (font != null)
     207            area.setFont(font);
     208        if (resource == null)
     209            return area;
     210        BufferedReader in;
     211        try {
     212            in = new BufferedReader(new InputStreamReader(resource.openStream()));
     213            StringBuilder sb = new StringBuilder();
     214            for (String line = in.readLine(); line != null; line = in.readLine()) {
     215                sb.append(line);
     216                sb.append('\n');
     217            }
     218            area.setText(sb.toString());
     219            area.setCaretPosition(0);
     220        } catch (IOException e) {
     221            e.printStackTrace();
     222        }
     223        return area;
     224    }
    225225}
  • trunk/src/org/openstreetmap/josm/actions/AddNodeAction.java

    r1138 r1169  
    4141
    4242/**
    43  * This action displays a dialog where the user can enter a latitude and longitude, 
     43 * This action displays a dialog where the user can enter a latitude and longitude,
    4444 * and when ok is pressed, a new node is created at the specified position.
    4545 */
     
    4747
    4848    public AddNodeAction() {
    49         super(tr("Add Node"), "addnode", tr("Add a node by entering latitude and longitude."),
    50                 Shortcut.registerShortcut("addnode", tr("Edit: {0}", tr("Add Node")), KeyEvent.VK_D, Shortcut.GROUP_EDIT,
    51                 Shortcut.SHIFT_DEFAULT), true);
     49        super(tr("Add Node"), "addnode", tr("Add a node by entering latitude and longitude."),
     50        Shortcut.registerShortcut("addnode", tr("Edit: {0}", tr("Add Node")), KeyEvent.VK_D, Shortcut.GROUP_EDIT,
     51        Shortcut.SHIFT_DEFAULT), true);
    5252    }
    5353
    54         public void actionPerformed(ActionEvent e) {   
     54    public void actionPerformed(ActionEvent e) {
    5555        JPanel p = new JPanel(new GridBagLayout());
    5656        p.add(new JLabel("<html>"+
    57                 tr("Enter the coordinates for the new node.") + 
    58                 "<br>" + tr("Use decimal degrees.") + 
    59                 "<br>" + tr("Negative values denote Western/Southern hemisphere.")), 
     57                tr("Enter the coordinates for the new node.") +
     58                "<br>" + tr("Use decimal degrees.") +
     59                "<br>" + tr("Negative values denote Western/Southern hemisphere.")),
    6060                GBC.eol());
    61        
     61
    6262        p.add(new JLabel(tr("Latitude")), GBC.std().insets(0,10,5,0));
    6363        final JTextField lat = new JTextField(12);
     
    6565        p.add(new JLabel(tr("Longitude")), GBC.std().insets(0,0,5,10));
    6666        final JTextField lon = new JTextField(12);
    67         p.add(lon, GBC.eol().insets(0,0,0,10));   
    68        
     67        p.add(lon, GBC.eol().insets(0,0,0,10));
     68
    6969        Node nnew = null;
    7070
     
    7979            } catch (Exception ex) { }
    8080        }
    81        
    82                 /* Now execute the commands to add the dupicated contents of the paste buffer to the map */
    83                
    84                 Main.main.undoRedo.add(new AddCommand(nnew));
    85                 Main.ds.setSelected(nnew);
    86                 Main.map.mapView.repaint();
     81
     82        /* Now execute the commands to add the dupicated contents of the paste buffer to the map */
     83
     84        Main.main.undoRedo.add(new AddCommand(nnew));
     85        Main.ds.setSelected(nnew);
     86        Main.map.mapView.repaint();
    8787    }
    8888}
  • trunk/src/org/openstreetmap/josm/actions/AlignInCircleAction.java

    r1160 r1169  
    2424/**
    2525 * Aligns all selected nodes within a circle. (Useful for roundabouts)
    26  * 
     26 *
    2727 * @author Matthew Newton
    2828 * @author Petr Dlouhý
     
    3131
    3232    public AlignInCircleAction() {
    33         super(tr("Align Nodes in Circle"), "aligncircle", tr("Move the selected nodes into a circle."), 
    34                 Shortcut.registerShortcut("tools:aligncircle", tr("Tool: {0}", tr("Align Nodes in Circle")), 
     33        super(tr("Align Nodes in Circle"), "aligncircle", tr("Move the selected nodes into a circle."),
     34                Shortcut.registerShortcut("tools:aligncircle", tr("Tool: {0}", tr("Align Nodes in Circle")),
    3535                        KeyEvent.VK_O, Shortcut.GROUP_EDIT), true);
    3636    }
  • trunk/src/org/openstreetmap/josm/actions/AlignInLineAction.java

    r1084 r1169  
    2929public final class AlignInLineAction extends JosmAction {
    3030
    31         public AlignInLineAction() {
    32                 super(tr("Align Nodes in Line"), "alignline", tr("Move the selected nodes onto a line."),
    33                 Shortcut.registerShortcut("tools:alignline", tr("Tool: {0}", tr("Align Nodes in Line")), KeyEvent.VK_L, Shortcut.GROUP_EDIT), true);
    34         }
     31    public AlignInLineAction() {
     32        super(tr("Align Nodes in Line"), "alignline", tr("Move the selected nodes onto a line."),
     33        Shortcut.registerShortcut("tools:alignline", tr("Tool: {0}", tr("Align Nodes in Line")), KeyEvent.VK_L, Shortcut.GROUP_EDIT), true);
     34    }
    3535
    36         /**
    37         * The general algorithm here is to find the two selected nodes
    38         * that are furthest apart, and then to align all other selected
    39         * nodes onto the straight line between these nodes.
    40         */
    41         public void actionPerformed(ActionEvent e) {
    42                 Collection<OsmPrimitive> sel = Main.ds.getSelected();
    43                 Collection<Node> nodes = new LinkedList<Node>();
    44                 Collection<Node> itnodes = new LinkedList<Node>();
    45                 for (OsmPrimitive osm : sel)
    46                         if (osm instanceof Node) {
    47                                 nodes.add((Node)osm);
    48                                 itnodes.add((Node)osm);
    49                         }
    50                 // special case if no single nodes are selected and exactly one way is:
    51                 // then use the way's nodes
    52                 if ((nodes.size() == 0) && (sel.size() == 1))
    53                         for (OsmPrimitive osm : sel)
    54                                 if (osm instanceof Way) {
    55                                         nodes.addAll(((Way)osm).nodes);
    56                                         itnodes.addAll(((Way)osm).nodes);
    57                                 }
    58                 if (nodes.size() < 3) {
    59                         JOptionPane.showMessageDialog(Main.parent, tr("Please select at least three nodes."));
    60                         return;
    61                 }
     36    /**
     37    * The general algorithm here is to find the two selected nodes
     38    * that are furthest apart, and then to align all other selected
     39    * nodes onto the straight line between these nodes.
     40    */
     41    public void actionPerformed(ActionEvent e) {
     42        Collection<OsmPrimitive> sel = Main.ds.getSelected();
     43        Collection<Node> nodes = new LinkedList<Node>();
     44        Collection<Node> itnodes = new LinkedList<Node>();
     45        for (OsmPrimitive osm : sel)
     46            if (osm instanceof Node) {
     47                nodes.add((Node)osm);
     48                itnodes.add((Node)osm);
     49            }
     50        // special case if no single nodes are selected and exactly one way is:
     51        // then use the way's nodes
     52        if ((nodes.size() == 0) && (sel.size() == 1))
     53            for (OsmPrimitive osm : sel)
     54                if (osm instanceof Way) {
     55                    nodes.addAll(((Way)osm).nodes);
     56                    itnodes.addAll(((Way)osm).nodes);
     57                }
     58        if (nodes.size() < 3) {
     59            JOptionPane.showMessageDialog(Main.parent, tr("Please select at least three nodes."));
     60            return;
     61        }
    6262
    63                 // Find from the selected nodes two that are the furthest apart.
    64                 // Let's call them A and B.
    65                 double distance = 0;
     63        // Find from the selected nodes two that are the furthest apart.
     64        // Let's call them A and B.
     65        double distance = 0;
    6666
    67                 Node nodea = null;
    68                 Node nodeb = null;
     67        Node nodea = null;
     68        Node nodeb = null;
    6969
    70                 for (Node n : nodes) {
    71                         itnodes.remove(n);
    72                         for (Node m : itnodes) {
    73                                 double dist = Math.sqrt(n.eastNorth.distance(m.eastNorth));
    74                                 if (dist > distance) {
    75                                         nodea = n;
    76                                         nodeb = m;
    77                                         distance = dist;
    78                                 }
    79                         }
    80                 }
     70        for (Node n : nodes) {
     71            itnodes.remove(n);
     72            for (Node m : itnodes) {
     73                double dist = Math.sqrt(n.eastNorth.distance(m.eastNorth));
     74                if (dist > distance) {
     75                    nodea = n;
     76                    nodeb = m;
     77                    distance = dist;
     78                }
     79            }
     80        }
    8181
    82                 // Remove the nodes A and B from the list of nodes to move
    83                 nodes.remove(nodea);
    84                 nodes.remove(nodeb);
     82        // Remove the nodes A and B from the list of nodes to move
     83        nodes.remove(nodea);
     84        nodes.remove(nodeb);
    8585
    86                 // Find out co-ords of A and B
    87                 double ax = nodea.eastNorth.east();
    88                 double ay = nodea.eastNorth.north();
    89                 double bx = nodeb.eastNorth.east();
    90                 double by = nodeb.eastNorth.north();
     86        // Find out co-ords of A and B
     87        double ax = nodea.eastNorth.east();
     88        double ay = nodea.eastNorth.north();
     89        double bx = nodeb.eastNorth.east();
     90        double by = nodeb.eastNorth.north();
    9191
    92                 // A list of commands to do
    93                 Collection<Command> cmds = new LinkedList<Command>();
     92        // A list of commands to do
     93        Collection<Command> cmds = new LinkedList<Command>();
    9494
    95                 // OK, for each node to move, work out where to move it!
    96                 for (Node n : nodes) {
    97                         // Get existing co-ords of node to move
    98                         double nx = n.eastNorth.east();
    99                         double ny = n.eastNorth.north();
     95        // OK, for each node to move, work out where to move it!
     96        for (Node n : nodes) {
     97            // Get existing co-ords of node to move
     98            double nx = n.eastNorth.east();
     99            double ny = n.eastNorth.north();
    100100
    101                         if (ax == bx) {
    102                                 // Special case if AB is vertical...
    103                                 nx = ax;
    104                         } else if (ay == by) {
    105                                 // ...or horizontal
    106                                 ny = ay;
    107                         } else {
    108                                 // Otherwise calculate position by solving y=mx+c
    109                                 double m1 = (by - ay) / (bx - ax);
    110                                 double c1 = ay - (ax * m1);
    111                                 double m2 = (-1) / m1;
    112                                 double c2 = n.eastNorth.north() - (n.eastNorth.east() * m2);
     101            if (ax == bx) {
     102                // Special case if AB is vertical...
     103                nx = ax;
     104            } else if (ay == by) {
     105                // ...or horizontal
     106                ny = ay;
     107            } else {
     108                // Otherwise calculate position by solving y=mx+c
     109                double m1 = (by - ay) / (bx - ax);
     110                double c1 = ay - (ax * m1);
     111                double m2 = (-1) / m1;
     112                double c2 = n.eastNorth.north() - (n.eastNorth.east() * m2);
    113113
    114                                 nx = (c2 - c1) / (m1 - m2);
    115                                 ny = (m1 * nx) + c1;
    116                         }
     114                nx = (c2 - c1) / (m1 - m2);
     115                ny = (m1 * nx) + c1;
     116            }
    117117
    118                         // Add the command to move the node to its new position.
    119                         cmds.add(new MoveCommand(n, nx - n.eastNorth.east(), ny - n.eastNorth.north() ));
    120                 }
     118            // Add the command to move the node to its new position.
     119            cmds.add(new MoveCommand(n, nx - n.eastNorth.east(), ny - n.eastNorth.north() ));
     120        }
    121121
    122                 // Do it!
    123                 Main.main.undoRedo.add(new SequenceCommand(tr("Align Nodes in Line"), cmds));
    124                 Main.map.repaint();
    125         }
     122        // Do it!
     123        Main.main.undoRedo.add(new SequenceCommand(tr("Align Nodes in Line"), cmds));
     124        Main.map.repaint();
     125    }
    126126}
  • trunk/src/org/openstreetmap/josm/actions/AutoScaleAction.java

    r1084 r1169  
    4747    public AutoScaleAction(String mode) {
    4848        super(tr("Zoom to {0}", tr(mode)), "dialogs/autoscale/" + mode, tr("Zoom the view to {0}.", tr(mode)),
    49                                 Shortcut.registerShortcut("view:zoom"+mode, tr("View: {0}", tr("Zoom to {0}", tr(mode))), getModeShortcut(mode), Shortcut.GROUP_EDIT), true);
     49                Shortcut.registerShortcut("view:zoom"+mode, tr("View: {0}", tr("Zoom to {0}", tr(mode))), getModeShortcut(mode), Shortcut.GROUP_EDIT), true);
    5050        String modeHelp = Character.toUpperCase(mode.charAt(0)) + mode.substring(1);
    5151        putValue("help", "Action/AutoScale/" + modeHelp);
  • trunk/src/org/openstreetmap/josm/actions/CombineWayAction.java

    r1084 r1169  
    4949public class CombineWayAction extends JosmAction implements SelectionChangedListener {
    5050
    51         public CombineWayAction() {
    52                 super(tr("Combine Way"), "combineway", tr("Combine several ways into one."),
    53                 Shortcut.registerShortcut("tools:combineway", tr("Tool: {0}", tr("Combine Way")), KeyEvent.VK_C, Shortcut.GROUP_EDIT), true);
    54                 DataSet.selListeners.add(this);
    55         }
    56 
    57         public void actionPerformed(ActionEvent event) {
    58                 Collection<OsmPrimitive> selection = Main.ds.getSelected();
    59                 LinkedList<Way> selectedWays = new LinkedList<Way>();
    60 
    61                 for (OsmPrimitive osm : selection)
    62                         if (osm instanceof Way)
    63                                 selectedWays.add((Way)osm);
    64 
    65                 if (selectedWays.size() < 2) {
    66                         JOptionPane.showMessageDialog(Main.parent, tr("Please select at least two ways to combine."));
    67                         return;
    68                 }
    69 
    70                 // Check whether all ways have identical relationship membership. More
    71                 // specifically: If one of the selected ways is a member of relation X
    72                 // in role Y, then all selected ways must be members of X in role Y.
    73 
    74                 // FIXME: In a later revision, we should display some sort of conflict
    75                 // dialog like we do for tags, to let the user choose which relations
    76                 // should be kept.
    77 
    78                 // Step 1, iterate over all relations and figure out which of our
    79                 // selected ways are members of a relation.
    80                 HashMap<Pair<Relation,String>, HashSet<Way>> backlinks =
    81                         new HashMap<Pair<Relation,String>, HashSet<Way>>();
    82                 HashSet<Relation> relationsUsingWays = new HashSet<Relation>();
    83                 for (Relation r : Main.ds.relations) {
    84                         if (r.deleted || r.incomplete) continue;
    85                         for (RelationMember rm : r.members) {
    86                                 if (rm.member instanceof Way) {
    87                                         for(Way w : selectedWays) {
    88                                                 if (rm.member == w) {
    89                                                         Pair<Relation,String> pair = new Pair<Relation,String>(r, rm.role);
    90                                                         HashSet<Way> waylinks = new HashSet<Way>();
    91                                                         if (backlinks.containsKey(pair)) {
    92                                                                 waylinks = backlinks.get(pair);
    93                                                         } else {
    94                                                                 waylinks = new HashSet<Way>();
    95                                                                 backlinks.put(pair, waylinks);
    96                                                         }
    97                                                         waylinks.add(w);
    98 
    99                                                         // this is just a cache for later use
    100                                                         relationsUsingWays.add(r);
    101                                                 }
    102                                         }
    103                                 }
    104                         }
    105                 }
    106 
    107                 // Complain to the user if the ways don't have equal memberships.
    108                 for (HashSet<Way> waylinks : backlinks.values()) {
    109                         if (!waylinks.containsAll(selectedWays)) {
    110                                 int option = JOptionPane.showConfirmDialog(Main.parent,
    111                                         tr("The selected ways have differing relation memberships.  "
    112                                                 + "Do you still want to combine them?"),
    113                                         tr("Combine ways with different memberships?"),
    114                                         JOptionPane.YES_NO_OPTION);
    115                                 if (option == JOptionPane.YES_OPTION)
    116                                         break;
    117                                 return;
    118                         }
    119                 }
    120 
    121                 // collect properties for later conflict resolving
    122                 Map<String, Set<String>> props = new TreeMap<String, Set<String>>();
    123                 for (Way w : selectedWays) {
    124                         for (Entry<String,String> e : w.entrySet()) {
    125                                 if (!props.containsKey(e.getKey()))
    126                                         props.put(e.getKey(), new TreeSet<String>());
    127                                 props.get(e.getKey()).add(e.getValue());
    128                         }
    129                 }
    130 
    131                 List<Node> nodeList = null;
    132                 Object firstTry = actuallyCombineWays(selectedWays, false);
    133                 if (firstTry instanceof List) {
    134                         nodeList = (List<Node>) firstTry;
    135                 } else {
    136                         Object secondTry = actuallyCombineWays(selectedWays, true);
    137                         if (secondTry instanceof List) {
    138                                 int option = JOptionPane.showConfirmDialog(Main.parent,
    139                                         tr("The ways can not be combined in their current directions.  "
    140                                         + "Do you want to reverse some of them?"), tr("Change directions?"),
    141                                         JOptionPane.YES_NO_OPTION);
    142                                 if (option != JOptionPane.YES_OPTION) {
    143                                         return;
    144                                 }
    145                                 nodeList = (List<Node>) secondTry;
    146                         } else {
    147                                 JOptionPane.showMessageDialog(Main.parent, secondTry);
    148                                 return;
    149                         }
    150                 }
    151 
    152                 // Find the most appropriate way to modify.
    153 
    154                 // Eventually this might want to be the way with the longest
    155                 // history or the longest selected way but for now just attempt
    156                 // to reuse an existing id.
    157                 Way modifyWay = selectedWays.peek();
    158                 for (Way w : selectedWays) {
    159                         modifyWay = w;
    160                         if (w.id != 0) break;
    161                 }
    162                 Way newWay = new Way(modifyWay);
    163 
    164                 newWay.nodes.clear();
    165                 newWay.nodes.addAll(nodeList);
    166 
    167                 // display conflict dialog
    168                 Map<String, JComboBox> components = new HashMap<String, JComboBox>();
    169                 JPanel p = new JPanel(new GridBagLayout());
    170                 for (Entry<String, Set<String>> e : props.entrySet()) {
    171                         if (TigerUtils.isTigerTag(e.getKey())) {
    172                                 String combined = TigerUtils.combineTags(e.getKey(), e.getValue());
    173                                 newWay.put(e.getKey(), combined);
    174                         } else if (e.getValue().size() > 1) {
    175                                 if("created_by".equals(e.getKey()))
    176                                 {
    177                                         newWay.put("created_by", "JOSM");
    178                                 }
    179                                 else
    180                                 {
    181                                         JComboBox c = new JComboBox(e.getValue().toArray());
    182                                         c.setEditable(true);
    183                                         p.add(new JLabel(e.getKey()), GBC.std());
    184                                         p.add(Box.createHorizontalStrut(10), GBC.std());
    185                                         p.add(c, GBC.eol());
    186                                         components.put(e.getKey(), c);
    187                                 }
    188                         } else
    189                                 newWay.put(e.getKey(), e.getValue().iterator().next());
    190                 }
    191 
    192                 if (!components.isEmpty()) {
    193                         int answer = JOptionPane.showConfirmDialog(Main.parent, p, tr("Enter values for all conflicts."), JOptionPane.OK_CANCEL_OPTION);
    194                         if (answer != JOptionPane.OK_OPTION)
    195                                 return;
    196                         for (Entry<String, JComboBox> e : components.entrySet())
    197                                 newWay.put(e.getKey(), e.getValue().getEditor().getItem().toString());
    198                 }
    199 
    200                 LinkedList<Command> cmds = new LinkedList<Command>();
    201                 LinkedList<Way> deletedWays = new LinkedList<Way>(selectedWays);
    202                 deletedWays.remove(modifyWay);
    203                 cmds.add(new DeleteCommand(deletedWays));
    204                 cmds.add(new ChangeCommand(modifyWay, newWay));
    205 
    206                 // modify all relations containing the now-deleted ways
    207                 for (Relation r : relationsUsingWays) {
    208                         Relation newRel = new Relation(r);
    209                         newRel.members.clear();
    210                         HashSet<String> rolesToReAdd = new HashSet<String>();
    211                         for (RelationMember rm : r.members) {
    212                                 // Don't copy the member if it to one of our ways, just keep a
    213                                 // note to re-add it later on.
    214                                 if (selectedWays.contains(rm.member)) {
    215                                         rolesToReAdd.add(rm.role);
    216                                 } else {
    217                                         newRel.members.add(rm);
    218                                 }
    219                         }
    220                         for (String role : rolesToReAdd) {
    221                                 newRel.members.add(new RelationMember(role, modifyWay));
    222                         }
    223                         cmds.add(new ChangeCommand(r, newRel));
    224                 }
    225                 Main.main.undoRedo.add(new SequenceCommand(tr("Combine {0} ways", selectedWays.size()), cmds));
    226                 Main.ds.setSelected(modifyWay);
    227         }
    228 
    229         /**
    230         * @return a message if combining failed, else a list of nodes.
    231         */
    232         private Object actuallyCombineWays(List<Way> ways, boolean ignoreDirection) {
    233                 // Battle plan:
    234                 //  1. Split the ways into small chunks of 2 nodes and weed out
    235                 //         duplicates.
    236                 //  2. Take a chunk and see if others could be appended or prepended,
    237                 //         if so, do it and remove it from the list of remaining chunks.
    238                 //         Rather, rinse, repeat.
    239                 //  3. If this algorithm does not produce a single way,
    240                 //     complain to the user.
    241                 //  4. Profit!
    242 
    243                 HashSet<Pair<Node,Node>> chunkSet = new HashSet<Pair<Node,Node>>();
    244                 for (Way w : ways)
    245                         chunkSet.addAll(w.getNodePairs(ignoreDirection));
    246 
    247                 LinkedList<Pair<Node,Node>> chunks = new LinkedList<Pair<Node,Node>>(chunkSet);
    248 
    249                 if (chunks.isEmpty()) {
    250                         return tr("All the ways were empty");
    251                 }
    252 
    253                 List<Node> nodeList = Pair.toArrayList(chunks.poll());
    254                 while (!chunks.isEmpty()) {
    255                         ListIterator<Pair<Node,Node>> it = chunks.listIterator();
    256                         boolean foundChunk = false;
    257                         while (it.hasNext()) {
    258                                 Pair<Node,Node> curChunk = it.next();
    259                                 if (curChunk.a == nodeList.get(nodeList.size() - 1)) { // append
    260                                         nodeList.add(curChunk.b);
    261                                 } else if (curChunk.b == nodeList.get(0)) { // prepend
    262                                         nodeList.add(0, curChunk.a);
    263                                 } else if (ignoreDirection && curChunk.b == nodeList.get(nodeList.size() - 1)) { // append
    264                                         nodeList.add(curChunk.a);
    265                                 } else if (ignoreDirection && curChunk.a == nodeList.get(0)) { // prepend
    266                                         nodeList.add(0, curChunk.b);
    267                                 } else {
    268                                         continue;
    269                                 }
    270 
    271                                 foundChunk = true;
    272                                 it.remove();
    273                                 break;
    274                         }
    275                         if (!foundChunk) break;
    276                 }
    277 
    278                 if (!chunks.isEmpty()) {
    279                         return tr("Could not combine ways "
    280                                 + "(They could not be merged into a single string of nodes)");
    281                 }
    282 
    283                 return nodeList;
    284         }
    285 
    286         /**
    287         * Enable the "Combine way" menu option if more then one way is selected
    288         */
    289         public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) {
    290                 boolean first = false;
    291                 for (OsmPrimitive osm : newSelection) {
    292                         if (osm instanceof Way) {
    293                                 if (first) {
    294                                         setEnabled(true);
    295                                         return;
    296                                 }
    297                                 first = true;
    298                         }
    299                 }
    300                 setEnabled(false);
    301         }
     51    public CombineWayAction() {
     52        super(tr("Combine Way"), "combineway", tr("Combine several ways into one."),
     53        Shortcut.registerShortcut("tools:combineway", tr("Tool: {0}", tr("Combine Way")), KeyEvent.VK_C, Shortcut.GROUP_EDIT), true);
     54        DataSet.selListeners.add(this);
     55    }
     56
     57    public void actionPerformed(ActionEvent event) {
     58        Collection<OsmPrimitive> selection = Main.ds.getSelected();
     59        LinkedList<Way> selectedWays = new LinkedList<Way>();
     60
     61        for (OsmPrimitive osm : selection)
     62            if (osm instanceof Way)
     63                selectedWays.add((Way)osm);
     64
     65        if (selectedWays.size() < 2) {
     66            JOptionPane.showMessageDialog(Main.parent, tr("Please select at least two ways to combine."));
     67            return;
     68        }
     69
     70        // Check whether all ways have identical relationship membership. More
     71        // specifically: If one of the selected ways is a member of relation X
     72        // in role Y, then all selected ways must be members of X in role Y.
     73
     74        // FIXME: In a later revision, we should display some sort of conflict
     75        // dialog like we do for tags, to let the user choose which relations
     76        // should be kept.
     77
     78        // Step 1, iterate over all relations and figure out which of our
     79        // selected ways are members of a relation.
     80        HashMap<Pair<Relation,String>, HashSet<Way>> backlinks =
     81            new HashMap<Pair<Relation,String>, HashSet<Way>>();
     82        HashSet<Relation> relationsUsingWays = new HashSet<Relation>();
     83        for (Relation r : Main.ds.relations) {
     84            if (r.deleted || r.incomplete) continue;
     85            for (RelationMember rm : r.members) {
     86                if (rm.member instanceof Way) {
     87                    for(Way w : selectedWays) {
     88                        if (rm.member == w) {
     89                            Pair<Relation,String> pair = new Pair<Relation,String>(r, rm.role);
     90                            HashSet<Way> waylinks = new HashSet<Way>();
     91                            if (backlinks.containsKey(pair)) {
     92                                waylinks = backlinks.get(pair);
     93                            } else {
     94                                waylinks = new HashSet<Way>();
     95                                backlinks.put(pair, waylinks);
     96                            }
     97                            waylinks.add(w);
     98
     99                            // this is just a cache for later use
     100                            relationsUsingWays.add(r);
     101                        }
     102                    }
     103                }
     104            }
     105        }
     106
     107        // Complain to the user if the ways don't have equal memberships.
     108        for (HashSet<Way> waylinks : backlinks.values()) {
     109            if (!waylinks.containsAll(selectedWays)) {
     110                int option = JOptionPane.showConfirmDialog(Main.parent,
     111                    tr("The selected ways have differing relation memberships.  "
     112                        + "Do you still want to combine them?"),
     113                    tr("Combine ways with different memberships?"),
     114                    JOptionPane.YES_NO_OPTION);
     115                if (option == JOptionPane.YES_OPTION)
     116                    break;
     117                return;
     118            }
     119        }
     120
     121        // collect properties for later conflict resolving
     122        Map<String, Set<String>> props = new TreeMap<String, Set<String>>();
     123        for (Way w : selectedWays) {
     124            for (Entry<String,String> e : w.entrySet()) {
     125                if (!props.containsKey(e.getKey()))
     126                    props.put(e.getKey(), new TreeSet<String>());
     127                props.get(e.getKey()).add(e.getValue());
     128            }
     129        }
     130
     131        List<Node> nodeList = null;
     132        Object firstTry = actuallyCombineWays(selectedWays, false);
     133        if (firstTry instanceof List) {
     134            nodeList = (List<Node>) firstTry;
     135        } else {
     136            Object secondTry = actuallyCombineWays(selectedWays, true);
     137            if (secondTry instanceof List) {
     138                int option = JOptionPane.showConfirmDialog(Main.parent,
     139                    tr("The ways can not be combined in their current directions.  "
     140                    + "Do you want to reverse some of them?"), tr("Change directions?"),
     141                    JOptionPane.YES_NO_OPTION);
     142                if (option != JOptionPane.YES_OPTION) {
     143                    return;
     144                }
     145                nodeList = (List<Node>) secondTry;
     146            } else {
     147                JOptionPane.showMessageDialog(Main.parent, secondTry);
     148                return;
     149            }
     150        }
     151
     152        // Find the most appropriate way to modify.
     153
     154        // Eventually this might want to be the way with the longest
     155        // history or the longest selected way but for now just attempt
     156        // to reuse an existing id.
     157        Way modifyWay = selectedWays.peek();
     158        for (Way w : selectedWays) {
     159            modifyWay = w;
     160            if (w.id != 0) break;
     161        }
     162        Way newWay = new Way(modifyWay);
     163
     164        newWay.nodes.clear();
     165        newWay.nodes.addAll(nodeList);
     166
     167        // display conflict dialog
     168        Map<String, JComboBox> components = new HashMap<String, JComboBox>();
     169        JPanel p = new JPanel(new GridBagLayout());
     170        for (Entry<String, Set<String>> e : props.entrySet()) {
     171            if (TigerUtils.isTigerTag(e.getKey())) {
     172                String combined = TigerUtils.combineTags(e.getKey(), e.getValue());
     173                newWay.put(e.getKey(), combined);
     174            } else if (e.getValue().size() > 1) {
     175                if("created_by".equals(e.getKey()))
     176                {
     177                    newWay.put("created_by", "JOSM");
     178                }
     179                else
     180                {
     181                    JComboBox c = new JComboBox(e.getValue().toArray());
     182                    c.setEditable(true);
     183                    p.add(new JLabel(e.getKey()), GBC.std());
     184                    p.add(Box.createHorizontalStrut(10), GBC.std());
     185                    p.add(c, GBC.eol());
     186                    components.put(e.getKey(), c);
     187                }
     188            } else
     189                newWay.put(e.getKey(), e.getValue().iterator().next());
     190        }
     191
     192        if (!components.isEmpty()) {
     193            int answer = JOptionPane.showConfirmDialog(Main.parent, p, tr("Enter values for all conflicts."), JOptionPane.OK_CANCEL_OPTION);
     194            if (answer != JOptionPane.OK_OPTION)
     195                return;
     196            for (Entry<String, JComboBox> e : components.entrySet())
     197                newWay.put(e.getKey(), e.getValue().getEditor().getItem().toString());
     198        }
     199
     200        LinkedList<Command> cmds = new LinkedList<Command>();
     201        LinkedList<Way> deletedWays = new LinkedList<Way>(selectedWays);
     202        deletedWays.remove(modifyWay);
     203        cmds.add(new DeleteCommand(deletedWays));
     204        cmds.add(new ChangeCommand(modifyWay, newWay));
     205
     206        // modify all relations containing the now-deleted ways
     207        for (Relation r : relationsUsingWays) {
     208            Relation newRel = new Relation(r);
     209            newRel.members.clear();
     210            HashSet<String> rolesToReAdd = new HashSet<String>();
     211            for (RelationMember rm : r.members) {
     212                // Don't copy the member if it to one of our ways, just keep a
     213                // note to re-add it later on.
     214                if (selectedWays.contains(rm.member)) {
     215                    rolesToReAdd.add(rm.role);
     216                } else {
     217                    newRel.members.add(rm);
     218                }
     219            }
     220            for (String role : rolesToReAdd) {
     221                newRel.members.add(new RelationMember(role, modifyWay));
     222            }
     223            cmds.add(new ChangeCommand(r, newRel));
     224        }
     225        Main.main.undoRedo.add(new SequenceCommand(tr("Combine {0} ways", selectedWays.size()), cmds));
     226        Main.ds.setSelected(modifyWay);
     227    }
     228
     229    /**
     230    * @return a message if combining failed, else a list of nodes.
     231    */
     232    private Object actuallyCombineWays(List<Way> ways, boolean ignoreDirection) {
     233        // Battle plan:
     234        //  1. Split the ways into small chunks of 2 nodes and weed out
     235        //     duplicates.
     236        //  2. Take a chunk and see if others could be appended or prepended,
     237        //     if so, do it and remove it from the list of remaining chunks.
     238        //     Rather, rinse, repeat.
     239        //  3. If this algorithm does not produce a single way,
     240        //     complain to the user.
     241        //  4. Profit!
     242
     243        HashSet<Pair<Node,Node>> chunkSet = new HashSet<Pair<Node,Node>>();
     244        for (Way w : ways)
     245            chunkSet.addAll(w.getNodePairs(ignoreDirection));
     246
     247        LinkedList<Pair<Node,Node>> chunks = new LinkedList<Pair<Node,Node>>(chunkSet);
     248
     249        if (chunks.isEmpty()) {
     250            return tr("All the ways were empty");
     251        }
     252
     253        List<Node> nodeList = Pair.toArrayList(chunks.poll());
     254        while (!chunks.isEmpty()) {
     255            ListIterator<Pair<Node,Node>> it = chunks.listIterator();
     256            boolean foundChunk = false;
     257            while (it.hasNext()) {
     258                Pair<Node,Node> curChunk = it.next();
     259                if (curChunk.a == nodeList.get(nodeList.size() - 1)) { // append
     260                    nodeList.add(curChunk.b);
     261                } else if (curChunk.b == nodeList.get(0)) { // prepend
     262                    nodeList.add(0, curChunk.a);
     263                } else if (ignoreDirection && curChunk.b == nodeList.get(nodeList.size() - 1)) { // append
     264                    nodeList.add(curChunk.a);
     265                } else if (ignoreDirection && curChunk.a == nodeList.get(0)) { // prepend
     266                    nodeList.add(0, curChunk.b);
     267                } else {
     268                    continue;
     269                }
     270
     271                foundChunk = true;
     272                it.remove();
     273                break;
     274            }
     275            if (!foundChunk) break;
     276        }
     277
     278        if (!chunks.isEmpty()) {
     279            return tr("Could not combine ways "
     280                + "(They could not be merged into a single string of nodes)");
     281        }
     282
     283        return nodeList;
     284    }
     285
     286    /**
     287    * Enable the "Combine way" menu option if more then one way is selected
     288    */
     289    public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) {
     290        boolean first = false;
     291        for (OsmPrimitive osm : newSelection) {
     292            if (osm instanceof Way) {
     293                if (first) {
     294                    setEnabled(true);
     295                    return;
     296                }
     297                first = true;
     298            }
     299        }
     300        setEnabled(false);
     301    }
    302302}
  • trunk/src/org/openstreetmap/josm/actions/CopyAction.java

    r1084 r1169  
    2828public final class CopyAction extends JosmAction implements SelectionChangedListener {
    2929
    30         private LinkedList<JosmAction> listeners;
     30    private LinkedList<JosmAction> listeners;
    3131
    32         public CopyAction() {
    33                 super(tr("Copy"), "copy",
    34                                 tr("Copy selected objects to paste buffer."),
    35                                 Shortcut.registerShortcut("system:copy", tr("Edit: {0}", tr("Copy")), KeyEvent.VK_C, Shortcut.GROUP_MENU), true);
    36                 setEnabled(false);
    37                 DataSet.selListeners.add(this);
    38                 listeners = new LinkedList<JosmAction>();
    39         }
     32    public CopyAction() {
     33        super(tr("Copy"), "copy",
     34                tr("Copy selected objects to paste buffer."),
     35                Shortcut.registerShortcut("system:copy", tr("Edit: {0}", tr("Copy")), KeyEvent.VK_C, Shortcut.GROUP_MENU), true);
     36        setEnabled(false);
     37        DataSet.selListeners.add(this);
     38        listeners = new LinkedList<JosmAction>();
     39    }
    4040
    41         @Override public void addListener(JosmAction a) {
    42                 listeners.add(a);
    43         }
     41    @Override public void addListener(JosmAction a) {
     42        listeners.add(a);
     43    }
    4444
    45         public void actionPerformed(ActionEvent e) {
    46                 Collection<OsmPrimitive> sel = Main.ds.getSelected();
    47                 if (sel.isEmpty()) {
    48                         JOptionPane.showMessageDialog(Main.parent,
    49                                         tr("Please select something to copy."));
    50                         return;
    51                 }
     45    public void actionPerformed(ActionEvent e) {
     46        Collection<OsmPrimitive> sel = Main.ds.getSelected();
     47        if (sel.isEmpty()) {
     48            JOptionPane.showMessageDialog(Main.parent,
     49                    tr("Please select something to copy."));
     50            return;
     51        }
    5252
    53                 /* New pasteBuffer - will be assigned to the global one at the end */
    54                 final DataSet pasteBuffer = new DataSet();
    55                 final HashMap<OsmPrimitive,OsmPrimitive> map = new HashMap<OsmPrimitive,OsmPrimitive>();
    56                 /* temporarily maps old nodes to new so we can do a true deep copy */
     53        /* New pasteBuffer - will be assigned to the global one at the end */
     54        final DataSet pasteBuffer = new DataSet();
     55        final HashMap<OsmPrimitive,OsmPrimitive> map = new HashMap<OsmPrimitive,OsmPrimitive>();
     56        /* temporarily maps old nodes to new so we can do a true deep copy */
    5757
    58                 /* scan the selected objects, mapping them to copies; when copying a way or relation,
    59                 * the copy references the copies of their child objects */
    60                 new Visitor(){
    61                         public void visit(Node n) {
    62                                 /* check if already in pasteBuffer - e.g. two ways are selected which share a node;
    63                                 * or a way and a node in that way is selected, we'll see it twice, once via the
    64                                 * way and once directly; and so on. */
    65                                 if (map.containsKey(n)) { return; }
    66                                 Node nnew = new Node(n);
    67                                 map.put(n, nnew);
    68                                 pasteBuffer.addPrimitive(nnew);
    69                         }
    70                         public void visit(Way w) {
    71                                 /* check if already in pasteBuffer - could have come from a relation, and directly etc. */
    72                                 if (map.containsKey(w)) { return; }
    73                                 Way wnew = new Way();
    74                                 wnew.cloneFrom(w);
    75                                 wnew.nodes.clear();
    76                                 List<Node> nodes = new ArrayList<Node>();
    77                                 for (Node n : w.nodes) {
    78                                         if (! map.containsKey(n)) {
    79                                                 n.visit(this);
    80                                         }
    81                                         nodes.add((Node)map.get(n));
    82                                 }
    83                                 wnew.nodes.clear();
    84                                 wnew.nodes.addAll(nodes);
    85                                 pasteBuffer.addPrimitive(wnew);
    86                         }
    87                         public void visit(Relation e) {
    88                                 if (map.containsKey(e)) { return; }
    89                                 Relation enew = new Relation(e);
    90                                 List<RelationMember> members = new ArrayList<RelationMember>();
    91                                 for (RelationMember m : e.members) {
    92                                         if (! map.containsKey(m.member)) {
    93                                                 m.member.visit(this);
    94                                         }
    95                                         RelationMember mnew = new RelationMember(m);
    96                                         mnew.member = map.get(m.member);
    97                                         members.add(mnew);
    98                                 }
    99                                 enew.members.addAll(members);
    100                                 pasteBuffer.addPrimitive(enew);
    101                         }
    102                         public void visitAll() {
    103                                 for (OsmPrimitive osm : Main.ds.getSelected())
    104                                         osm.visit(this);
    105                         }
    106                 }.visitAll();
     58        /* scan the selected objects, mapping them to copies; when copying a way or relation,
     59        * the copy references the copies of their child objects */
     60        new Visitor(){
     61            public void visit(Node n) {
     62                /* check if already in pasteBuffer - e.g. two ways are selected which share a node;
     63                * or a way and a node in that way is selected, we'll see it twice, once via the
     64                * way and once directly; and so on. */
     65                if (map.containsKey(n)) { return; }
     66                Node nnew = new Node(n);
     67                map.put(n, nnew);
     68                pasteBuffer.addPrimitive(nnew);
     69            }
     70            public void visit(Way w) {
     71                /* check if already in pasteBuffer - could have come from a relation, and directly etc. */
     72                if (map.containsKey(w)) { return; }
     73                Way wnew = new Way();
     74                wnew.cloneFrom(w);
     75                wnew.nodes.clear();
     76                List<Node> nodes = new ArrayList<Node>();
     77                for (Node n : w.nodes) {
     78                    if (! map.containsKey(n)) {
     79                        n.visit(this);
     80                    }
     81                    nodes.add((Node)map.get(n));
     82                }
     83                wnew.nodes.clear();
     84                wnew.nodes.addAll(nodes);
     85                pasteBuffer.addPrimitive(wnew);
     86            }
     87            public void visit(Relation e) {
     88                if (map.containsKey(e)) { return; }
     89                Relation enew = new Relation(e);
     90                List<RelationMember> members = new ArrayList<RelationMember>();
     91                for (RelationMember m : e.members) {
     92                    if (! map.containsKey(m.member)) {
     93                        m.member.visit(this);
     94                    }
     95                    RelationMember mnew = new RelationMember(m);
     96                    mnew.member = map.get(m.member);
     97                    members.add(mnew);
     98                }
     99                enew.members.addAll(members);
     100                pasteBuffer.addPrimitive(enew);
     101            }
     102            public void visitAll() {
     103                for (OsmPrimitive osm : Main.ds.getSelected())
     104                    osm.visit(this);
     105            }
     106        }.visitAll();
    107107
    108                 Main.pasteBuffer = pasteBuffer;
    109                 Main.main.menu.paste.setEnabled(true); /* now we have a paste buffer we can make paste available */
     108        Main.pasteBuffer = pasteBuffer;
     109        Main.main.menu.paste.setEnabled(true); /* now we have a paste buffer we can make paste available */
    110110
    111                 for(JosmAction a : listeners) {
    112                         a.pasteBufferChanged(Main.pasteBuffer);
    113                 }
    114         }
     111        for(JosmAction a : listeners) {
     112            a.pasteBufferChanged(Main.pasteBuffer);
     113        }
     114    }
    115115
    116         public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) {
    117                 setEnabled(! newSelection.isEmpty());
    118         }
     116    public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) {
     117        setEnabled(! newSelection.isEmpty());
     118    }
    119119}
  • trunk/src/org/openstreetmap/josm/actions/CreateCircleAction.java

    r1084 r1169  
    3434public final class CreateCircleAction extends JosmAction {
    3535
    36         public CreateCircleAction() {
    37                 super(tr("Create Circle"), "createcircle", tr("Create a circle from three selected nodes."),
    38                 Shortcut.registerShortcut("tools:createcircle", tr("Tool: {0}", tr("Create Circle")), KeyEvent.VK_O, Shortcut.GROUP_EDIT, Shortcut.SHIFT_DEFAULT), true);
    39         }
     36    public CreateCircleAction() {
     37        super(tr("Create Circle"), "createcircle", tr("Create a circle from three selected nodes."),
     38        Shortcut.registerShortcut("tools:createcircle", tr("Tool: {0}", tr("Create Circle")), KeyEvent.VK_O, Shortcut.GROUP_EDIT, Shortcut.SHIFT_DEFAULT), true);
     39    }
    4040
    41         private double calcang(double xc, double yc, double x, double y) {
    42                 // calculate the angle from xc|yc to x|y
    43                 if (xc == x && yc == y) {
    44                         return 0; // actually invalid, but we won't have this case in this context
    45                 }
    46                 double yd = Math.abs(y - yc);
    47                 if (yd == 0 && xc < x) {
    48                         return 0;
    49                 }
    50                 if (yd == 0 && xc > x) {
    51                         return Math.PI;
    52                 }
    53                 double xd = Math.abs(x - xc);
    54                 double a = Math.atan2(xd, yd);
    55                 if (y > yc) {
    56                         a = Math.PI - a;
    57                 }
    58                 if (x < xc) {
    59                         a = -a;
    60                 }
    61                 a = 1.5*Math.PI + a;
    62                 if (a < 0) {
    63                         a += 2*Math.PI;
    64                 }
    65                 if (a >= 2*Math.PI) {
    66                         a -= 2*Math.PI;
    67                 }
    68                 return a;
    69         }
     41    private double calcang(double xc, double yc, double x, double y) {
     42        // calculate the angle from xc|yc to x|y
     43        if (xc == x && yc == y) {
     44            return 0; // actually invalid, but we won't have this case in this context
     45        }
     46        double yd = Math.abs(y - yc);
     47        if (yd == 0 && xc < x) {
     48            return 0;
     49        }
     50        if (yd == 0 && xc > x) {
     51            return Math.PI;
     52        }
     53        double xd = Math.abs(x - xc);
     54        double a = Math.atan2(xd, yd);
     55        if (y > yc) {
     56            a = Math.PI - a;
     57        }
     58        if (x < xc) {
     59            a = -a;
     60        }
     61        a = 1.5*Math.PI + a;
     62        if (a < 0) {
     63            a += 2*Math.PI;
     64        }
     65        if (a >= 2*Math.PI) {
     66            a -= 2*Math.PI;
     67        }
     68        return a;
     69    }
    7070
    71         public void actionPerformed(ActionEvent e) {
    72                 int numberOfNodesInCircle = Integer.parseInt(Main.pref.get("createcircle.nodecount", "8"));
    73                 if (numberOfNodesInCircle < 1) {
    74                         numberOfNodesInCircle = 1;
    75                 } else if (numberOfNodesInCircle > 100) {
    76                         numberOfNodesInCircle = 100;
    77                 }
     71    public void actionPerformed(ActionEvent e) {
     72        int numberOfNodesInCircle = Integer.parseInt(Main.pref.get("createcircle.nodecount", "8"));
     73        if (numberOfNodesInCircle < 1) {
     74            numberOfNodesInCircle = 1;
     75        } else if (numberOfNodesInCircle > 100) {
     76            numberOfNodesInCircle = 100;
     77        }
    7878
    79                 Collection<OsmPrimitive> sel = Main.ds.getSelected();
    80                 Collection<Node> nodes = new LinkedList<Node>();
    81                 Way existingWay = null;
     79        Collection<OsmPrimitive> sel = Main.ds.getSelected();
     80        Collection<Node> nodes = new LinkedList<Node>();
     81        Way existingWay = null;
    8282
    83                 for (OsmPrimitive osm : sel)
    84                         if (osm instanceof Node)
    85                                 nodes.add((Node)osm);
     83        for (OsmPrimitive osm : sel)
     84            if (osm instanceof Node)
     85                nodes.add((Node)osm);
    8686
    87                 // special case if no single nodes are selected and exactly one way is:
    88                 // then use the way's nodes
    89                 if ((nodes.size() == 0) && (sel.size() == 1))
    90                         for (OsmPrimitive osm : sel)
    91                                 if (osm instanceof Way) {
    92                                         existingWay = ((Way)osm);
    93                                         for (Node n : ((Way)osm).nodes)
    94                                         {
    95                                                 if(!nodes.contains(n))
    96                                                         nodes.add(n);
    97                                         }
    98                                 }
     87        // special case if no single nodes are selected and exactly one way is:
     88        // then use the way's nodes
     89        if ((nodes.size() == 0) && (sel.size() == 1))
     90            for (OsmPrimitive osm : sel)
     91                if (osm instanceof Way) {
     92                    existingWay = ((Way)osm);
     93                    for (Node n : ((Way)osm).nodes)
     94                    {
     95                        if(!nodes.contains(n))
     96                            nodes.add(n);
     97                    }
     98                }
    9999
    100                 if (nodes.size() != 3) {
    101                         JOptionPane.showMessageDialog(Main.parent, tr("Please select exactly three nodes or one way with exactly three nodes."));
    102                         return;
    103                 }
     100        if (nodes.size() != 3) {
     101            JOptionPane.showMessageDialog(Main.parent, tr("Please select exactly three nodes or one way with exactly three nodes."));
     102            return;
     103        }
    104104
    105                 // let's get some shorter names
    106                 Node   n1 = ((Node)nodes.toArray()[0]);
    107                 double x1 = n1.eastNorth.east();
    108                 double y1 = n1.eastNorth.north();
    109                 Node   n2 = ((Node)nodes.toArray()[1]);
    110                 double x2 = n2.eastNorth.east();
    111                 double y2 = n2.eastNorth.north();
    112                 Node   n3 = ((Node)nodes.toArray()[2]);
    113                 double x3 = n3.eastNorth.east();
    114                 double y3 = n3.eastNorth.north();
     105        // let's get some shorter names
     106        Node   n1 = ((Node)nodes.toArray()[0]);
     107        double x1 = n1.eastNorth.east();
     108        double y1 = n1.eastNorth.north();
     109        Node   n2 = ((Node)nodes.toArray()[1]);
     110        double x2 = n2.eastNorth.east();
     111        double y2 = n2.eastNorth.north();
     112        Node   n3 = ((Node)nodes.toArray()[2]);
     113        double x3 = n3.eastNorth.east();
     114        double y3 = n3.eastNorth.north();
    115115
    116                 // calculate the center (xc/yc)
    117                 double s = 0.5*((x2 - x3)*(x1 - x3) - (y2 - y3)*(y3 - y1));
    118                 double sUnder = (x1 - x2)*(y3 - y1) - (y2 - y1)*(x1 - x3);
     116        // calculate the center (xc/yc)
     117        double s = 0.5*((x2 - x3)*(x1 - x3) - (y2 - y3)*(y3 - y1));
     118        double sUnder = (x1 - x2)*(y3 - y1) - (y2 - y1)*(x1 - x3);
    119119
    120                 if (sUnder == 0) {
    121                         JOptionPane.showMessageDialog(Main.parent, tr("Those nodes are not in a circle."));
    122                         return;
    123                 }
     120        if (sUnder == 0) {
     121            JOptionPane.showMessageDialog(Main.parent, tr("Those nodes are not in a circle."));
     122            return;
     123        }
    124124
    125                 s /= sUnder;
     125        s /= sUnder;
    126126
    127                 double xc = 0.5*(x1 + x2) + s*(y2 - y1);
    128                 double yc = 0.5*(y1 + y2) + s*(x1 - x2);
     127        double xc = 0.5*(x1 + x2) + s*(y2 - y1);
     128        double yc = 0.5*(y1 + y2) + s*(x1 - x2);
    129129
    130                 // calculate the radius (r)
    131                 double r = Math.sqrt(Math.pow(xc-x1,2) + Math.pow(yc-y1,2));
     130        // calculate the radius (r)
     131        double r = Math.sqrt(Math.pow(xc-x1,2) + Math.pow(yc-y1,2));
    132132
    133                 // find where to put the existing nodes
    134                 double a1 = calcang(xc, yc, x1, y1);
    135                 double a2 = calcang(xc, yc, x2, y2);
    136                 double a3 = calcang(xc, yc, x3, y3);
    137                 if (a1 < a2) { double at = a1; Node nt = n1; a1 = a2; n1 = n2; a2 = at; n2 = nt; }
    138                 if (a2 < a3) { double at = a2; Node nt = n2; a2 = a3; n2 = n3; a3 = at; n3 = nt; }
    139                 if (a1 < a2) { double at = a1; Node nt = n1; a1 = a2; n1 = n2; a2 = at; n2 = nt; }
     133        // find where to put the existing nodes
     134        double a1 = calcang(xc, yc, x1, y1);
     135        double a2 = calcang(xc, yc, x2, y2);
     136        double a3 = calcang(xc, yc, x3, y3);
     137        if (a1 < a2) { double at = a1; Node nt = n1; a1 = a2; n1 = n2; a2 = at; n2 = nt; }
     138        if (a2 < a3) { double at = a2; Node nt = n2; a2 = a3; n2 = n3; a3 = at; n3 = nt; }
     139        if (a1 < a2) { double at = a1; Node nt = n1; a1 = a2; n1 = n2; a2 = at; n2 = nt; }
    140140
    141                 // now we can start doing thigs to OSM data
    142                 Collection<Command> cmds = new LinkedList<Command>();
     141        // now we can start doing thigs to OSM data
     142        Collection<Command> cmds = new LinkedList<Command>();
    143143
    144                 // build a way for the circle
    145                 Way wayToAdd;
    146                 if (existingWay == null) {
    147                         wayToAdd = new Way();
    148                 } else {
    149                         // re-use existing way if it was selected
    150                         wayToAdd = new Way(existingWay);
    151                         wayToAdd.nodes.clear();
    152                 }
    153                 for (int i = 1; i <= numberOfNodesInCircle; i++) {
    154                         double a = 2*Math.PI*(1.0 - i/(double)numberOfNodesInCircle); // "1-" to get it clock-wise
    155                         // insert existing nodes if they fit before this new node (999 means "already added this node")
    156                         if (a1 < 999 && a1 > a) {
    157                                 wayToAdd.nodes.add(n1);
    158                                 a1 = 999;
    159                         }
    160                         if (a2 < 999 && a2 > a) {
    161                                 wayToAdd.nodes.add(n2);
    162                                 a2 = 999;
    163                         }
    164                         if (a3 < 999 && a3 > a) {
    165                                 wayToAdd.nodes.add(n3);
    166                                 a3 = 999;
    167                         }
    168                         // get the position of the new node and insert it
    169                         double x = xc + r*Math.cos(a);
    170                         double y = yc + r*Math.sin(a);
    171                         Node n = new Node(Main.proj.eastNorth2latlon(new EastNorth(x,y)));
    172                         wayToAdd.nodes.add(n);
    173                         cmds.add(new AddCommand(n));
    174                 }
    175                 wayToAdd.nodes.add(wayToAdd.nodes.get(0)); // close the circle
    176                 if (existingWay == null) {
    177                         cmds.add(new AddCommand(wayToAdd));
    178                 } else {
    179                         cmds.add(new ChangeCommand(existingWay, wayToAdd));
    180                 }
     144        // build a way for the circle
     145        Way wayToAdd;
     146        if (existingWay == null) {
     147            wayToAdd = new Way();
     148        } else {
     149            // re-use existing way if it was selected
     150            wayToAdd = new Way(existingWay);
     151            wayToAdd.nodes.clear();
     152        }
     153        for (int i = 1; i <= numberOfNodesInCircle; i++) {
     154            double a = 2*Math.PI*(1.0 - i/(double)numberOfNodesInCircle); // "1-" to get it clock-wise
     155            // insert existing nodes if they fit before this new node (999 means "already added this node")
     156            if (a1 < 999 && a1 > a) {
     157                wayToAdd.nodes.add(n1);
     158                a1 = 999;
     159            }
     160            if (a2 < 999 && a2 > a) {
     161                wayToAdd.nodes.add(n2);
     162                a2 = 999;
     163            }
     164            if (a3 < 999 && a3 > a) {
     165                wayToAdd.nodes.add(n3);
     166                a3 = 999;
     167            }
     168            // get the position of the new node and insert it
     169            double x = xc + r*Math.cos(a);
     170            double y = yc + r*Math.sin(a);
     171            Node n = new Node(Main.proj.eastNorth2latlon(new EastNorth(x,y)));
     172            wayToAdd.nodes.add(n);
     173            cmds.add(new AddCommand(n));
     174        }
     175        wayToAdd.nodes.add(wayToAdd.nodes.get(0)); // close the circle
     176        if (existingWay == null) {
     177            cmds.add(new AddCommand(wayToAdd));
     178        } else {
     179            cmds.add(new ChangeCommand(existingWay, wayToAdd));
     180        }
    181181
    182                 Main.main.undoRedo.add(new SequenceCommand(tr("Create Circle"), cmds));
    183                 Main.map.repaint();
    184         }
     182        Main.main.undoRedo.add(new SequenceCommand(tr("Create Circle"), cmds));
     183        Main.map.repaint();
     184    }
    185185}
  • trunk/src/org/openstreetmap/josm/actions/DeleteAction.java

    r1087 r1169  
    1717public final class DeleteAction extends JosmAction implements SelectionChangedListener {
    1818
    19         public DeleteAction() {
    20                 super(tr("Delete"), "dialogs/delete", tr("Delete selected objects."),
    21                 Shortcut.registerShortcut("system:delete", tr("Edit: {0}", tr("Delete")), KeyEvent.VK_DELETE, Shortcut.GROUP_DIRECT), true);
     19    public DeleteAction() {
     20        super(tr("Delete"), "dialogs/delete", tr("Delete selected objects."),
     21        Shortcut.registerShortcut("system:delete", tr("Edit: {0}", tr("Delete")), KeyEvent.VK_DELETE, Shortcut.GROUP_DIRECT), true);
    2222        DataSet.selListeners.add(this);
    23                 setEnabled(false);
    24         }
     23        setEnabled(false);
     24    }
    2525
    26         public void actionPerformed(ActionEvent e) {
    27                 new org.openstreetmap.josm.actions.mapmode.DeleteAction(Main.map)
    28                         .doActionPerformed(e);
    29         }
     26    public void actionPerformed(ActionEvent e) {
     27        new org.openstreetmap.josm.actions.mapmode.DeleteAction(Main.map)
     28                .doActionPerformed(e);
     29    }
    3030    public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) {
    3131        setEnabled(! newSelection.isEmpty());
  • trunk/src/org/openstreetmap/josm/actions/DiskAccessAction.java

    r1084 r1169  
    1717abstract public class DiskAccessAction extends JosmAction {
    1818
    19         public DiskAccessAction(String name, String iconName, String tooltip, Shortcut shortcut) {
    20                 super(name, iconName, tooltip, shortcut, true);
    21         }
     19    public DiskAccessAction(String name, String iconName, String tooltip, Shortcut shortcut) {
     20        super(name, iconName, tooltip, shortcut, true);
     21    }
    2222
    23         @Deprecated
    24         public DiskAccessAction(String name, String iconName, String tooltip, int shortcut, int modifiers) {
    25                 super(name, iconName, tooltip, shortcut, modifiers, true);
    26         }
     23    @Deprecated
     24    public DiskAccessAction(String name, String iconName, String tooltip, int shortcut, int modifiers) {
     25        super(name, iconName, tooltip, shortcut, modifiers, true);
     26    }
    2727
    28         protected static JFileChooser createAndOpenFileChooser(boolean open, boolean multiple, String title) {
    29                 String curDir = Main.pref.get("lastDirectory");
    30                 if (curDir.equals(""))
    31                         curDir = ".";
    32                 JFileChooser fc = new JFileChooser(new File(curDir));
    33                 if(title != null)
    34                         fc.setDialogTitle(title);
     28    protected static JFileChooser createAndOpenFileChooser(boolean open, boolean multiple, String title) {
     29        String curDir = Main.pref.get("lastDirectory");
     30        if (curDir.equals(""))
     31            curDir = ".";
     32        JFileChooser fc = new JFileChooser(new File(curDir));
     33        if(title != null)
     34            fc.setDialogTitle(title);
    3535
    36                 fc.setMultiSelectionEnabled(multiple);
    37                 for (int i = 0; i < ExtensionFileFilter.filters.length; ++i)
    38                         fc.addChoosableFileFilter(ExtensionFileFilter.filters[i]);
    39                 fc.setAcceptAllFileFilterUsed(true);
     36        fc.setMultiSelectionEnabled(multiple);
     37        for (int i = 0; i < ExtensionFileFilter.filters.length; ++i)
     38            fc.addChoosableFileFilter(ExtensionFileFilter.filters[i]);
     39        fc.setAcceptAllFileFilterUsed(true);
    4040
    41                 int answer = open ? fc.showOpenDialog(Main.parent) : fc.showSaveDialog(Main.parent);
    42                 if (answer != JFileChooser.APPROVE_OPTION)
    43                         return null;
     41        int answer = open ? fc.showOpenDialog(Main.parent) : fc.showSaveDialog(Main.parent);
     42        if (answer != JFileChooser.APPROVE_OPTION)
     43            return null;
    4444
    45                 if (!fc.getCurrentDirectory().getAbsolutePath().equals(curDir))
    46                         Main.pref.put("lastDirectory", fc.getCurrentDirectory().getAbsolutePath());
     45        if (!fc.getCurrentDirectory().getAbsolutePath().equals(curDir))
     46            Main.pref.put("lastDirectory", fc.getCurrentDirectory().getAbsolutePath());
    4747
    48                 if (!open) {
    49                         File file = fc.getSelectedFile();
    50                         if (file == null || (file.exists() && JOptionPane.YES_OPTION !=
    51                                         JOptionPane.showConfirmDialog(Main.parent, tr("File exists. Overwrite?"), tr("Overwrite"), JOptionPane.YES_NO_OPTION)))
    52                                 return null;
    53                 }
     48        if (!open) {
     49            File file = fc.getSelectedFile();
     50            if (file == null || (file.exists() && JOptionPane.YES_OPTION !=
     51                    JOptionPane.showConfirmDialog(Main.parent, tr("File exists. Overwrite?"), tr("Overwrite"), JOptionPane.YES_NO_OPTION)))
     52                return null;
     53        }
    5454
    55                 return fc;
    56         }
     55        return fc;
     56    }
    5757}
  • trunk/src/org/openstreetmap/josm/actions/DownloadAction.java

    r1084 r1169  
    2828public class DownloadAction extends JosmAction {
    2929
    30         public DownloadDialog dialog;
     30    public DownloadDialog dialog;
    3131
    32         public DownloadAction() {
    33                 super(tr("Download from OSM ..."), "download", tr("Download map data from the OSM server."),
    34                 Shortcut.registerShortcut("file:download", tr("File: {0}", tr("Download from OSM ...")), KeyEvent.VK_D, Shortcut.GROUPS_ALT1+Shortcut.GROUP_HOTKEY), true);
    35         }
     32    public DownloadAction() {
     33        super(tr("Download from OSM ..."), "download", tr("Download map data from the OSM server."),
     34        Shortcut.registerShortcut("file:download", tr("File: {0}", tr("Download from OSM ...")), KeyEvent.VK_D, Shortcut.GROUPS_ALT1+Shortcut.GROUP_HOTKEY), true);
     35    }
    3636
    37         public void actionPerformed(ActionEvent e) {
    38                 dialog = new DownloadDialog();
     37    public void actionPerformed(ActionEvent e) {
     38        dialog = new DownloadDialog();
    3939
    40                 JPanel downPanel = new JPanel(new GridBagLayout());
    41                 downPanel.add(dialog, GBC.eol().fill(GBC.BOTH));
     40        JPanel downPanel = new JPanel(new GridBagLayout());
     41        downPanel.add(dialog, GBC.eol().fill(GBC.BOTH));
    4242
    43                 JOptionPane pane = new JOptionPane(downPanel, JOptionPane.PLAIN_MESSAGE, JOptionPane.OK_CANCEL_OPTION);
    44                 JDialog dlg = pane.createDialog(Main.parent, tr("Download"));
    45                 dlg.setResizable(true);
    46                 dialog.setOptionPane(pane);
     43        JOptionPane pane = new JOptionPane(downPanel, JOptionPane.PLAIN_MESSAGE, JOptionPane.OK_CANCEL_OPTION);
     44        JDialog dlg = pane.createDialog(Main.parent, tr("Download"));
     45        dlg.setResizable(true);
     46        dialog.setOptionPane(pane);
    4747
    48                 if (dlg.getWidth() > 1000)
    49                         dlg.setSize(1000, dlg.getHeight());
    50                 if (dlg.getHeight() > 600)
    51                         dlg.setSize(dlg.getWidth(),600);
     48        if (dlg.getWidth() > 1000)
     49            dlg.setSize(1000, dlg.getHeight());
     50        if (dlg.getHeight() > 600)
     51            dlg.setSize(dlg.getWidth(),600);
    5252
    53                 boolean finish = false;
     53        boolean finish = false;
    5454        while (!finish) {
    5555            dlg.setVisible(true);
    5656            Main.pref.put("download.newlayer", dialog.newLayer.isSelected());
    57                 if (pane.getValue() instanceof Integer && (Integer)pane.getValue() == JOptionPane.OK_OPTION) {
    58                         Main.pref.put("download.tab", Integer.toString(dialog.getSelectedTab()));
    59                         for (DownloadTask task : dialog.downloadTasks) {
    60                                 Main.pref.put("download."+task.getPreferencesSuffix(), task.getCheckBox().isSelected());
    61                                 if (task.getCheckBox().isSelected()) {
    62                                         task.download(this, dialog.minlat, dialog.minlon, dialog.maxlat, dialog.maxlon);
    63                                         finish = true;
    64                                 }
    65                         }
    66                 } else
    67                         finish = true;
    68                 if (!finish)
    69                         JOptionPane.showMessageDialog(Main.parent, tr("Please select at least one task to download"));
     57            if (pane.getValue() instanceof Integer && (Integer)pane.getValue() == JOptionPane.OK_OPTION) {
     58                Main.pref.put("download.tab", Integer.toString(dialog.getSelectedTab()));
     59                for (DownloadTask task : dialog.downloadTasks) {
     60                    Main.pref.put("download."+task.getPreferencesSuffix(), task.getCheckBox().isSelected());
     61                    if (task.getCheckBox().isSelected()) {
     62                        task.download(this, dialog.minlat, dialog.minlon, dialog.maxlat, dialog.maxlon);
     63                        finish = true;
     64                    }
     65                }
     66            } else
     67                finish = true;
     68            if (!finish)
     69                JOptionPane.showMessageDialog(Main.parent, tr("Please select at least one task to download"));
    7070        }
    7171
    7272                dialog = null;
    73                 dlg.dispose();
    74         }
     73        dlg.dispose();
     74    }
    7575}
  • trunk/src/org/openstreetmap/josm/actions/DuplicateAction.java

    r1084 r1169  
    1818
    1919    public DuplicateAction() {
    20         super(tr("Duplicate"), "duplicate",
    21                         tr("Duplicate selection by copy and immediate paste."),
    22                         Shortcut.registerShortcut("system:duplicate", tr("Edit: {0}", tr("Duplicate")), KeyEvent.VK_D, Shortcut.GROUP_MENU), true);
    23         setEnabled(false);
    24                         DataSet.selListeners.add(this);
     20        super(tr("Duplicate"), "duplicate",
     21            tr("Duplicate selection by copy and immediate paste."),
     22            Shortcut.registerShortcut("system:duplicate", tr("Edit: {0}", tr("Duplicate")), KeyEvent.VK_D, Shortcut.GROUP_MENU), true);
     23        setEnabled(false);
     24            DataSet.selListeners.add(this);
    2525    }
    2626
    27         public void actionPerformed(ActionEvent e) {
    28                 Main.main.menu.copy.actionPerformed(e);
    29                 Main.main.menu.paste.actionPerformed(e);
     27    public void actionPerformed(ActionEvent e) {
     28        Main.main.menu.copy.actionPerformed(e);
     29        Main.main.menu.paste.actionPerformed(e);
    3030    }
    3131
    32         public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) {
    33                 setEnabled(! newSelection.isEmpty());
    34         }
     32    public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) {
     33        setEnabled(! newSelection.isEmpty());
     34    }
    3535}
  • trunk/src/org/openstreetmap/josm/actions/ExitAction.java

    r1084 r1169  
    1616 */
    1717public class ExitAction extends JosmAction {
    18         /**
    19         * Construct the action with "Exit" as label
    20         */
    21         public ExitAction() {
    22                 super(tr("Exit"), "exit", tr("Exit the application."),
    23                 Shortcut.registerShortcut("system:menuexit", tr("Exit"), KeyEvent.VK_Q, Shortcut.GROUP_MENU), true);
    24         }
     18    /**
     19    * Construct the action with "Exit" as label
     20    */
     21    public ExitAction() {
     22        super(tr("Exit"), "exit", tr("Exit the application."),
     23        Shortcut.registerShortcut("system:menuexit", tr("Exit"), KeyEvent.VK_Q, Shortcut.GROUP_MENU), true);
     24    }
    2525
    26         public void actionPerformed(ActionEvent e) {
    27                 if (!Main.breakBecauseUnsavedChanges())
    28                         System.exit(0);
    29         }
     26    public void actionPerformed(ActionEvent e) {
     27        if (!Main.breakBecauseUnsavedChanges())
     28            System.exit(0);
     29    }
    3030}
  • trunk/src/org/openstreetmap/josm/actions/ExtensionFileFilter.java

    r929 r1169  
    99
    1010/**
    11  * A file filter that filters after the extension. Also includes a list of file 
     11 * A file filter that filters after the extension. Also includes a list of file
    1212 * filters used in JOSM.
    13  * 
     13 *
    1414 * @author imi
    1515 */
    1616public class ExtensionFileFilter extends FileFilter {
    17         private final String extension;
    18         private final String description;
    19         public final String defaultExtension;
     17    private final String extension;
     18    private final String description;
     19    public final String defaultExtension;
    2020
    21         public static final int OSM = 0;
    22         public static final int GPX = 1;
    23         public static final int NMEA = 2;
    24        
    25         public static ExtensionFileFilter[] filters = {
    26                 new ExtensionFileFilter("osm,xml", "osm", tr("OSM Server Files (*.osm *.xml)")),
    27                 new ExtensionFileFilter("gpx,gpx.gz", "gpx", tr("GPX Files (*.gpx *.gpx.gz)")),
    28                 new ExtensionFileFilter("nmea,txt", "nmea", tr("NMEA-0183 Files (*.nmea *.txt)")),
    29         };
     21    public static final int OSM = 0;
     22    public static final int GPX = 1;
     23    public static final int NMEA = 2;
    3024
    31         /**
    32          * Construct an extension file filter by giving the extension to check after.
    33          *
    34          */
    35         public ExtensionFileFilter(String extension, String defExt, String description) {
    36                 this.extension = extension;
    37                 defaultExtension = defExt;
    38                 this.description = description;
    39         }
     25    public static ExtensionFileFilter[] filters = {
     26        new ExtensionFileFilter("osm,xml", "osm", tr("OSM Server Files (*.osm *.xml)")),
     27        new ExtensionFileFilter("gpx,gpx.gz", "gpx", tr("GPX Files (*.gpx *.gpx.gz)")),
     28        new ExtensionFileFilter("nmea,txt", "nmea", tr("NMEA-0183 Files (*.nmea *.txt)")),
     29    };
    4030
    41         public boolean acceptName(String filename) {
    42                 String name = filename.toLowerCase();
    43                 for (String ext : extension.split(","))
    44                         if (name.endsWith("."+ext))
    45                                 return true;
    46                 return false;
    47         }
     31    /**
     32     * Construct an extension file filter by giving the extension to check after.
     33     *
     34     */
     35    public ExtensionFileFilter(String extension, String defExt, String description) {
     36        this.extension = extension;
     37        defaultExtension = defExt;
     38        this.description = description;
     39    }
    4840
    49         @Override public boolean accept(File pathname) {
    50                 if (pathname.isDirectory())
    51                         return true;
    52                 return acceptName(pathname.getName());
    53         }
     41    public boolean acceptName(String filename) {
     42        String name = filename.toLowerCase();
     43        for (String ext : extension.split(","))
     44            if (name.endsWith("."+ext))
     45                return true;
     46        return false;
     47    }
    5448
    55         @Override public String getDescription() {
    56                 return description;
    57         }
     49    @Override public boolean accept(File pathname) {
     50        if (pathname.isDirectory())
     51            return true;
     52        return acceptName(pathname.getName());
     53    }
     54
     55    @Override public String getDescription() {
     56        return description;
     57    }
    5858}
  • trunk/src/org/openstreetmap/josm/actions/GpxExportAction.java

    r1084 r1169  
    4040public class GpxExportAction extends DiskAccessAction {
    4141
    42         private final static String warningGpl = "<html><font color='red' size='-2'>"+tr("Note: GPL is not compatible with the OSM license. Do not upload GPL licensed tracks.")+"</html>";
    43 
    44         private final Layer layer;
    45 
    46         public GpxExportAction(Layer layer) {
    47                 super(tr("Export to GPX ..."), "exportgpx", tr("Export the data to GPX file."),
    48                 Shortcut.registerShortcut("file:exportgpx", tr("Export to GPX ..."), KeyEvent.VK_E, Shortcut.GROUP_MENU));
    49                 this.layer = layer;
    50         }
    51 
    52         public void actionPerformed(ActionEvent e) {
    53                 if (layer == null && Main.map == null) {
    54                         JOptionPane.showMessageDialog(Main.parent, tr("Nothing to export. Get some data first."));
    55                         return;
    56                 }
    57 
    58                 JFileChooser fc = createAndOpenFileChooser(false, false, null);
    59                 if (fc == null)
    60                         return;
    61                 File file = fc.getSelectedFile();
    62                 if (file == null)
    63                         return;
    64 
    65                 exportGpx(file, this.layer == null ? Main.main.editLayer() : this.layer);
    66         }
    67 
    68         public static void exportGpx(File file, Layer layer) {
    69                 String fn = file.getPath();
    70                 if (fn.indexOf('.') == -1) {
    71                         fn += ".gpx";
    72                         file = new File(fn);
    73                 }
    74 
    75                 // open the dialog asking for options
    76                 JPanel p = new JPanel(new GridBagLayout());
    77 
    78                 p.add(new JLabel(tr("gps track description")), GBC.eol());
    79                 JTextArea desc = new JTextArea(3,40);
    80                 desc.setWrapStyleWord(true);
    81                 desc.setLineWrap(true);
    82                 p.add(new JScrollPane(desc), GBC.eop().fill(GBC.BOTH));
    83 
    84                 JCheckBox author = new JCheckBox(tr("Add author information"), Main.pref.getBoolean("lastAddAuthor", true));
    85                 author.setSelected(true);
    86                 p.add(author, GBC.eol());
    87                 JLabel nameLabel = new JLabel(tr("Real name"));
    88                 p.add(nameLabel, GBC.std().insets(10,0,5,0));
    89                 JTextField authorName = new JTextField(Main.pref.get("lastAuthorName"));
    90                 p.add(authorName, GBC.eol().fill(GBC.HORIZONTAL));
    91                 JLabel emailLabel = new JLabel(tr("Email"));
    92                 p.add(emailLabel, GBC.std().insets(10,0,5,0));
    93                 JTextField email = new JTextField(Main.pref.get("osm-server.username"));
    94                 p.add(email, GBC.eol().fill(GBC.HORIZONTAL));
    95                 JLabel copyrightLabel = new JLabel(tr("Copyright (URL)"));
    96                 p.add(copyrightLabel, GBC.std().insets(10,0,5,0));
    97                 JTextField copyright = new JTextField();
    98                 p.add(copyright, GBC.std().fill(GBC.HORIZONTAL));
    99                 JButton predefined = new JButton(tr("Predefined"));
    100                 p.add(predefined, GBC.eol().insets(5,0,0,0));
    101                 JLabel copyrightYearLabel = new JLabel(tr("Copyright year"));
    102                 p.add(copyrightYearLabel, GBC.std().insets(10,0,5,5));
    103                 JTextField copyrightYear = new JTextField("");
    104                 p.add(copyrightYear, GBC.eol().fill(GBC.HORIZONTAL));
    105                 JLabel warning = new JLabel("<html><font size='-2'>&nbsp;</html");
    106                 p.add(warning, GBC.eol().fill(GBC.HORIZONTAL).insets(15,0,0,0));
    107                 addDependencies(author, authorName, email, copyright, predefined, copyrightYear, nameLabel, emailLabel, copyrightLabel, copyrightYearLabel, warning);
    108 
    109                 p.add(new JLabel(tr("Keywords")), GBC.eol());
    110                 JTextField keywords = new JTextField();
    111                 p.add(keywords, GBC.eop().fill(GBC.HORIZONTAL));
    112 
    113                 int answer = JOptionPane.showConfirmDialog(Main.parent, p, tr("Export options"), JOptionPane.OK_CANCEL_OPTION);
    114                 if (answer != JOptionPane.OK_OPTION)
    115                         return;
    116 
    117                 Main.pref.put("lastAddAuthor", author.isSelected());
    118                 if (authorName.getText().length() != 0)
    119                         Main.pref.put("lastAuthorName", authorName.getText());
    120                 if (copyright.getText().length() != 0)
    121                         Main.pref.put("lastCopyright", copyright.getText());
    122 
    123                 GpxData gpxData;
    124                 if (layer instanceof OsmDataLayer)
    125                         gpxData = ((OsmDataLayer)layer).toGpxData();
    126                 else if (layer instanceof GpxLayer)
    127                         gpxData = ((GpxLayer)layer).data;
    128                 else
    129                         gpxData = OsmDataLayer.toGpxData(Main.ds);
    130                 // TODO: add copyright, etc.
    131                 try {
    132                         FileOutputStream fo = new FileOutputStream(file);
    133                         new GpxWriter(fo).write(gpxData);
    134                         fo.flush();
    135                         fo.close();
    136                 } catch (IOException x) {
    137                         x.printStackTrace();
    138                         JOptionPane.showMessageDialog(Main.parent, tr("Error while exporting {0}", fn)+":\n"+x.getMessage(), tr("Error"), JOptionPane.ERROR_MESSAGE);
    139                 }
    140         }
    141 
    142         /**
    143         * Add all those listeners to handle the enable state of the fields.
    144         * @param copyrightYearLabel
    145         * @param copyrightLabel
    146         * @param emailLabel
    147         * @param nameLabel
    148         * @param warning
    149         */
    150         private static void addDependencies(
    151                         final JCheckBox author,
    152                         final JTextField authorName,
    153                         final JTextField email,
    154                         final JTextField copyright,
    155                         final JButton predefined,
    156                         final JTextField copyrightYear,
    157                         final JLabel nameLabel,
    158                         final JLabel emailLabel,
    159                         final JLabel copyrightLabel,
    160                         final JLabel copyrightYearLabel,
    161                         final JLabel warning) {
    162 
    163                 ActionListener authorActionListener = new ActionListener(){
    164                         public void actionPerformed(ActionEvent e) {
    165                                 boolean b = author.isSelected();
    166                                 authorName.setEnabled(b);
    167                                 email.setEnabled(b);
    168                                 nameLabel.setEnabled(b);
    169                                 emailLabel.setEnabled(b);
    170                                 authorName.setText(b ? Main.pref.get("lastAuthorName") : "");
    171                                 email.setText(b ? Main.pref.get("osm-server.username") : "");
    172 
    173                                 boolean authorSet = authorName.getText().length() != 0;
    174                                 enableCopyright(copyright, predefined, copyrightYear, copyrightLabel, copyrightYearLabel, warning, b && authorSet);
    175                         }
    176                 };
    177                 author.addActionListener(authorActionListener);
    178 
    179                 KeyAdapter authorNameListener = new KeyAdapter(){
    180                                         @Override public void keyReleased(KeyEvent e) {
    181                                                 boolean b = authorName.getText().length()!=0 && author.isSelected();
    182                                                 enableCopyright(copyright, predefined, copyrightYear, copyrightLabel, copyrightYearLabel, warning, b);
    183                                         }
    184                                 };
    185                 authorName.addKeyListener(authorNameListener);
    186 
    187                 predefined.addActionListener(new ActionListener(){
    188                         public void actionPerformed(ActionEvent e) {
    189                                 JList l = new JList(new String[]{"Creative Commons By-SA", "public domain", "GNU Lesser Public License (LGPL)", "BSD License (MIT/X11)"});
    190                                 l.setVisibleRowCount(4);
    191                                 l.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
    192                                 int answer = JOptionPane.showConfirmDialog(Main.parent, new JScrollPane(l),tr("Choose a predefined license"), JOptionPane.OK_CANCEL_OPTION);
    193                                 if (answer != JOptionPane.OK_OPTION || l.getSelectedIndex() == -1)
    194                                         return;
    195                                 final String[] urls = {
    196                                                 "http://creativecommons.org/licenses/by-sa/2.5",
    197                                                 "public domain",
    198                                                 "http://www.gnu.org/copyleft/lesser.html",
    199                                                 "http://www.opensource.org/licenses/bsd-license.php"};
    200                                 String license = "";
    201                                 for (int i : l.getSelectedIndices()) {
    202                                         if (i == 1) {
    203                                                 license = "public domain";
    204                                                 break;
    205                                         }
    206                                         license += license.length()==0 ? urls[i] : ", "+urls[i];
    207                                 }
    208                                 copyright.setText(license);
    209                                 copyright.setCaretPosition(0);
    210                         }
    211                 });
    212 
    213                 authorActionListener.actionPerformed(null);
    214                 authorNameListener.keyReleased(null);
    215         }
    216 
    217         private static void enableCopyright(final JTextField copyright, final JButton predefined, final JTextField copyrightYear, final JLabel copyrightLabel, final JLabel copyrightYearLabel, final JLabel warning, boolean enable) {
    218                 copyright.setEnabled(enable);
    219                 predefined.setEnabled(enable);
    220                 copyrightYear.setEnabled(enable);
    221                 copyrightLabel.setEnabled(enable);
    222                 copyrightYearLabel.setEnabled(enable);
    223                 warning.setText(enable ? warningGpl : "<html><font size='-2'>&nbsp;</html");
    224 
    225                 if (enable && copyrightYear.getText().length()==0) {
    226                         copyrightYear.setText(enable ? Integer.toString(Calendar.getInstance().get(Calendar.YEAR)) : "");
    227                 } else if (!enable)
    228                         copyrightYear.setText("");
    229 
    230                 if (enable && copyright.getText().length()==0) {
    231                         copyright.setText(enable ? Main.pref.get("lastCopyright", "http://creativecommons.org/licenses/by-sa/2.5") : "");
    232                         copyright.setCaretPosition(0);
    233                 } else if (!enable)
    234                         copyright.setText("");
    235         }
     42    private final static String warningGpl = "<html><font color='red' size='-2'>"+tr("Note: GPL is not compatible with the OSM license. Do not upload GPL licensed tracks.")+"</html>";
     43
     44    private final Layer layer;
     45
     46    public GpxExportAction(Layer layer) {
     47        super(tr("Export to GPX ..."), "exportgpx", tr("Export the data to GPX file."),
     48        Shortcut.registerShortcut("file:exportgpx", tr("Export to GPX ..."), KeyEvent.VK_E, Shortcut.GROUP_MENU));
     49        this.layer = layer;
     50    }
     51
     52    public void actionPerformed(ActionEvent e) {
     53        if (layer == null && Main.map == null) {
     54            JOptionPane.showMessageDialog(Main.parent, tr("Nothing to export. Get some data first."));
     55            return;
     56        }
     57
     58        JFileChooser fc = createAndOpenFileChooser(false, false, null);
     59        if (fc == null)
     60            return;
     61        File file = fc.getSelectedFile();
     62        if (file == null)
     63            return;
     64
     65        exportGpx(file, this.layer == null ? Main.main.editLayer() : this.layer);
     66    }
     67
     68    public static void exportGpx(File file, Layer layer) {
     69        String fn = file.getPath();
     70        if (fn.indexOf('.') == -1) {
     71            fn += ".gpx";
     72            file = new File(fn);
     73        }
     74
     75        // open the dialog asking for options
     76        JPanel p = new JPanel(new GridBagLayout());
     77
     78        p.add(new JLabel(tr("gps track description")), GBC.eol());
     79        JTextArea desc = new JTextArea(3,40);
     80        desc.setWrapStyleWord(true);
     81        desc.setLineWrap(true);
     82        p.add(new JScrollPane(desc), GBC.eop().fill(GBC.BOTH));
     83
     84        JCheckBox author = new JCheckBox(tr("Add author information"), Main.pref.getBoolean("lastAddAuthor", true));
     85        author.setSelected(true);
     86        p.add(author, GBC.eol());
     87        JLabel nameLabel = new JLabel(tr("Real name"));
     88        p.add(nameLabel, GBC.std().insets(10,0,5,0));
     89        JTextField authorName = new JTextField(Main.pref.get("lastAuthorName"));
     90        p.add(authorName, GBC.eol().fill(GBC.HORIZONTAL));
     91        JLabel emailLabel = new JLabel(tr("Email"));
     92        p.add(emailLabel, GBC.std().insets(10,0,5,0));
     93        JTextField email = new JTextField(Main.pref.get("osm-server.username"));
     94        p.add(email, GBC.eol().fill(GBC.HORIZONTAL));
     95        JLabel copyrightLabel = new JLabel(tr("Copyright (URL)"));
     96        p.add(copyrightLabel, GBC.std().insets(10,0,5,0));
     97        JTextField copyright = new JTextField();
     98        p.add(copyright, GBC.std().fill(GBC.HORIZONTAL));
     99        JButton predefined = new JButton(tr("Predefined"));
     100        p.add(predefined, GBC.eol().insets(5,0,0,0));
     101        JLabel copyrightYearLabel = new JLabel(tr("Copyright year"));
     102        p.add(copyrightYearLabel, GBC.std().insets(10,0,5,5));
     103        JTextField copyrightYear = new JTextField("");
     104        p.add(copyrightYear, GBC.eol().fill(GBC.HORIZONTAL));
     105        JLabel warning = new JLabel("<html><font size='-2'>&nbsp;</html");
     106        p.add(warning, GBC.eol().fill(GBC.HORIZONTAL).insets(15,0,0,0));
     107        addDependencies(author, authorName, email, copyright, predefined, copyrightYear, nameLabel, emailLabel, copyrightLabel, copyrightYearLabel, warning);
     108
     109        p.add(new JLabel(tr("Keywords")), GBC.eol());
     110        JTextField keywords = new JTextField();
     111        p.add(keywords, GBC.eop().fill(GBC.HORIZONTAL));
     112
     113        int answer = JOptionPane.showConfirmDialog(Main.parent, p, tr("Export options"), JOptionPane.OK_CANCEL_OPTION);
     114        if (answer != JOptionPane.OK_OPTION)
     115            return;
     116
     117        Main.pref.put("lastAddAuthor", author.isSelected());
     118        if (authorName.getText().length() != 0)
     119            Main.pref.put("lastAuthorName", authorName.getText());
     120        if (copyright.getText().length() != 0)
     121            Main.pref.put("lastCopyright", copyright.getText());
     122
     123        GpxData gpxData;
     124        if (layer instanceof OsmDataLayer)
     125            gpxData = ((OsmDataLayer)layer).toGpxData();
     126        else if (layer instanceof GpxLayer)
     127            gpxData = ((GpxLayer)layer).data;
     128        else
     129            gpxData = OsmDataLayer.toGpxData(Main.ds);
     130        // TODO: add copyright, etc.
     131        try {
     132            FileOutputStream fo = new FileOutputStream(file);
     133            new GpxWriter(fo).write(gpxData);
     134            fo.flush();
     135            fo.close();
     136        } catch (IOException x) {
     137            x.printStackTrace();
     138            JOptionPane.showMessageDialog(Main.parent, tr("Error while exporting {0}", fn)+":\n"+x.getMessage(), tr("Error"), JOptionPane.ERROR_MESSAGE);
     139        }
     140    }
     141
     142    /**
     143    * Add all those listeners to handle the enable state of the fields.
     144    * @param copyrightYearLabel
     145    * @param copyrightLabel
     146    * @param emailLabel
     147    * @param nameLabel
     148    * @param warning
     149    */
     150    private static void addDependencies(
     151            final JCheckBox author,
     152            final JTextField authorName,
     153            final JTextField email,
     154            final JTextField copyright,
     155            final JButton predefined,
     156            final JTextField copyrightYear,
     157            final JLabel nameLabel,
     158            final JLabel emailLabel,
     159            final JLabel copyrightLabel,
     160            final JLabel copyrightYearLabel,
     161            final JLabel warning) {
     162
     163        ActionListener authorActionListener = new ActionListener(){
     164            public void actionPerformed(ActionEvent e) {
     165                boolean b = author.isSelected();
     166                authorName.setEnabled(b);
     167                email.setEnabled(b);
     168                nameLabel.setEnabled(b);
     169                emailLabel.setEnabled(b);
     170                authorName.setText(b ? Main.pref.get("lastAuthorName") : "");
     171                email.setText(b ? Main.pref.get("osm-server.username") : "");
     172
     173                boolean authorSet = authorName.getText().length() != 0;
     174                enableCopyright(copyright, predefined, copyrightYear, copyrightLabel, copyrightYearLabel, warning, b && authorSet);
     175            }
     176        };
     177        author.addActionListener(authorActionListener);
     178
     179        KeyAdapter authorNameListener = new KeyAdapter(){
     180                    @Override public void keyReleased(KeyEvent e) {
     181                        boolean b = authorName.getText().length()!=0 && author.isSelected();
     182                        enableCopyright(copyright, predefined, copyrightYear, copyrightLabel, copyrightYearLabel, warning, b);
     183                    }
     184                };
     185        authorName.addKeyListener(authorNameListener);
     186
     187        predefined.addActionListener(new ActionListener(){
     188            public void actionPerformed(ActionEvent e) {
     189                JList l = new JList(new String[]{"Creative Commons By-SA", "public domain", "GNU Lesser Public License (LGPL)", "BSD License (MIT/X11)"});
     190                l.setVisibleRowCount(4);
     191                l.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
     192                int answer = JOptionPane.showConfirmDialog(Main.parent, new JScrollPane(l),tr("Choose a predefined license"), JOptionPane.OK_CANCEL_OPTION);
     193                if (answer != JOptionPane.OK_OPTION || l.getSelectedIndex() == -1)
     194                    return;
     195                final String[] urls = {
     196                        "http://creativecommons.org/licenses/by-sa/2.5",
     197                        "public domain",
     198                        "http://www.gnu.org/copyleft/lesser.html",
     199                        "http://www.opensource.org/licenses/bsd-license.php"};
     200                String license = "";
     201                for (int i : l.getSelectedIndices()) {
     202                    if (i == 1) {
     203                        license = "public domain";
     204                        break;
     205                    }
     206                    license += license.length()==0 ? urls[i] : ", "+urls[i];
     207                }
     208                copyright.setText(license);
     209                copyright.setCaretPosition(0);
     210            }
     211        });
     212
     213        authorActionListener.actionPerformed(null);
     214        authorNameListener.keyReleased(null);
     215    }
     216
     217    private static void enableCopyright(final JTextField copyright, final JButton predefined, final JTextField copyrightYear, final JLabel copyrightLabel, final JLabel copyrightYearLabel, final JLabel warning, boolean enable) {
     218        copyright.setEnabled(enable);
     219        predefined.setEnabled(enable);
     220        copyrightYear.setEnabled(enable);
     221        copyrightLabel.setEnabled(enable);
     222        copyrightYearLabel.setEnabled(enable);
     223        warning.setText(enable ? warningGpl : "<html><font size='-2'>&nbsp;</html");
     224
     225        if (enable && copyrightYear.getText().length()==0) {
     226            copyrightYear.setText(enable ? Integer.toString(Calendar.getInstance().get(Calendar.YEAR)) : "");
     227        } else if (!enable)
     228            copyrightYear.setText("");
     229
     230        if (enable && copyright.getText().length()==0) {
     231            copyright.setText(enable ? Main.pref.get("lastCopyright", "http://creativecommons.org/licenses/by-sa/2.5") : "");
     232            copyright.setCaretPosition(0);
     233        } else if (!enable)
     234            copyright.setText("");
     235    }
    236236}
  • trunk/src/org/openstreetmap/josm/actions/HelpAction.java

    r1163 r1169  
    4242public class HelpAction extends AbstractAction {
    4343
    44         public interface Helpful {
    45                 String helpTopic();
     44    public interface Helpful {
     45        String helpTopic();
    4646    }
    4747
    4848        private String languageCode = Main.getLanguageCodeU();
    49         private JFrame helpBrowser = new JFrame(tr("JOSM Online Help"));
    50         private String baseurl = Main.pref.get("help.baseurl", "http://josm.openstreetmap.de");
    51         private JEditorPane help = new JEditorPane();
    52         private WikiReader reader = new WikiReader(baseurl);
    53         private String url;
    54 
    55         public HelpAction() {
    56                 super(tr("Help"), ImageProvider.get("help"));
    57                 help.setEditable(false);
    58                 help.addHyperlinkListener(new HyperlinkListener(){
    59                         public void hyperlinkUpdate(HyperlinkEvent e) {
    60                                 if (e.getEventType() != HyperlinkEvent.EventType.ACTIVATED)
    61                                         return;
    62                                 if (e.getURL() == null)
    63                                         help.setText("<html>404 not found</html>");
    64                                 else if (e.getURL().toString().startsWith(WikiReader.JOSM_EXTERN))
    65                                         OpenBrowser.displayUrl("http://"+e.getURL().toString().substring(WikiReader.JOSM_EXTERN.length())+"?action=edit");
    66                                 else
    67                                         setHelpUrl(e.getURL().toString());
    68                         }
    69                 });
    70                 help.setContentType("text/html");
    71 
    72                 JPanel p = new JPanel(new BorderLayout());
    73                 helpBrowser.setContentPane(p);
    74 
    75                 p.add(new JScrollPane(help), BorderLayout.CENTER);
    76                 String[] bounds = Main.pref.get("help.window.bounds", "0,0,800,600").split(",");
    77                 helpBrowser.setBounds(
    78                                 Integer.parseInt(bounds[0]),
    79                                 Integer.parseInt(bounds[1]),
    80                                 Integer.parseInt(bounds[2]),
    81                                 Integer.parseInt(bounds[3]));
    82 
    83                 JPanel buttons = new JPanel();
    84                 p.add(buttons, BorderLayout.SOUTH);
    85                 createButton(buttons, tr("Open in Browser"));
    86                 createButton(buttons, tr("Edit"));
    87                 createButton(buttons, tr("Reload"));
    88 
    89                 helpBrowser.addWindowListener(new WindowAdapter(){
    90                         @Override public void windowClosing(WindowEvent e) {
    91                                 closeHelp();
    92                         }
    93                 });
     49    private JFrame helpBrowser = new JFrame(tr("JOSM Online Help"));
     50    private String baseurl = Main.pref.get("help.baseurl", "http://josm.openstreetmap.de");
     51    private JEditorPane help = new JEditorPane();
     52    private WikiReader reader = new WikiReader(baseurl);
     53    private String url;
     54
     55    public HelpAction() {
     56        super(tr("Help"), ImageProvider.get("help"));
     57        help.setEditable(false);
     58        help.addHyperlinkListener(new HyperlinkListener(){
     59            public void hyperlinkUpdate(HyperlinkEvent e) {
     60                if (e.getEventType() != HyperlinkEvent.EventType.ACTIVATED)
     61                    return;
     62                if (e.getURL() == null)
     63                    help.setText("<html>404 not found</html>");
     64                else if (e.getURL().toString().startsWith(WikiReader.JOSM_EXTERN))
     65                    OpenBrowser.displayUrl("http://"+e.getURL().toString().substring(WikiReader.JOSM_EXTERN.length())+"?action=edit");
     66                else
     67                    setHelpUrl(e.getURL().toString());
     68            }
     69        });
     70        help.setContentType("text/html");
     71
     72        JPanel p = new JPanel(new BorderLayout());
     73        helpBrowser.setContentPane(p);
     74
     75        p.add(new JScrollPane(help), BorderLayout.CENTER);
     76        String[] bounds = Main.pref.get("help.window.bounds", "0,0,800,600").split(",");
     77        helpBrowser.setBounds(
     78                Integer.parseInt(bounds[0]),
     79                Integer.parseInt(bounds[1]),
     80                Integer.parseInt(bounds[2]),
     81                Integer.parseInt(bounds[3]));
     82
     83        JPanel buttons = new JPanel();
     84        p.add(buttons, BorderLayout.SOUTH);
     85        createButton(buttons, tr("Open in Browser"));
     86        createButton(buttons, tr("Edit"));
     87        createButton(buttons, tr("Reload"));
     88
     89        helpBrowser.addWindowListener(new WindowAdapter(){
     90            @Override public void windowClosing(WindowEvent e) {
     91                closeHelp();
     92            }
     93        });
    9494
    9595        help.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "Close");
    9696        help.getActionMap().put("Close", new AbstractAction(){
    97                         public void actionPerformed(ActionEvent e) {
    98                                 closeHelp();
     97            public void actionPerformed(ActionEvent e) {
     98                closeHelp();
    9999            }
    100100        });
    101         }
    102 
    103         public void actionPerformed(ActionEvent e) {
    104                 if (tr("Open in Browser").equals(e.getActionCommand())) {
    105                         OpenBrowser.displayUrl(url);
    106                 } else if (tr("Edit").equals(e.getActionCommand())) {
    107                         if (!url.startsWith(baseurl)) {
    108                                 JOptionPane.showMessageDialog(Main.parent, tr("Can only edit help pages from JOSM Online Help"));
    109                                 return;
    110                         }
    111                         OpenBrowser.displayUrl(url+"?action=edit");
    112                 } else if (tr("Reload").equals(e.getActionCommand())) {
    113                         setHelpUrl(url);
    114                 } else if (e.getActionCommand() == null) {
    115                         String topic = null;
    116                         Point mouse = Main.parent.getMousePosition();
    117                         if (mouse != null)
    118                                 topic = contextSensitiveHelp(SwingUtilities.getDeepestComponentAt(Main.parent, mouse.x, mouse.y));
    119                         if (topic == null) {
    120                                 helpBrowser.setVisible(false);
    121                                 setHelpUrl(baseurl+"/wiki/Help");
    122                         } else
    123                                 help(topic);
    124                 } else {
    125                         helpBrowser.setVisible(false);
    126                         setHelpUrl(baseurl+"/wiki/Help");
    127                 }
    128         }
    129 
    130         /**
    131         * @return The topic of the help. <code>null</code> for "don't know"
    132         */
    133         private String contextSensitiveHelp(Object c) {
    134                 if (c instanceof Helpful)
    135                         return ((Helpful)c).helpTopic();
    136                 if (c instanceof JMenu)
    137                         return "Menu/"+((JMenu)c).getText();
    138                 if (c instanceof AbstractButton) {
    139                         AbstractButton b = (AbstractButton)c;
    140                         if (b.getClientProperty("help") != null)
    141                                 return (String)b.getClientProperty("help");
    142                         return contextSensitiveHelp(((AbstractButton)c).getAction());
    143                 }
    144                 if (c instanceof Action)
    145                         return (String)((Action)c).getValue("help");
    146                 if (c instanceof Component)
    147                         return contextSensitiveHelp(((Component)c).getParent());
    148                 return null;
    149     }
    150 
    151         /**
    152         * Displays the help (or browse on the already open help) on the online page
    153         * with the given help topic. Use this for larger help descriptions.
    154         */
    155         public void help(String topic) {
    156                 helpBrowser.setVisible(false);
    157                 setHelpUrl(baseurl+"/wiki/Help/"+topic);
    158         }
    159 
    160         /**
    161         * Set the content of the help window to a specific text (in html format)
    162         * @param url The url this content is the representation of
    163         */
    164         public void setHelpUrl(String url) {
    165                 int i = url.lastIndexOf("/")+1;
    166                 String title = url.substring(i);
    167                 if(!title.startsWith(languageCode))
    168                         title = languageCode + title;
    169                 String langurl = url.substring(0, i) + title;
    170                 if(langurl.equals(this.url) || langurl.equals(url))
    171                 {
    172                         this.url = url;
    173                         try {
    174                                 help.read(new StringReader(reader.read(url)), help.getEditorKit().createDefaultDocument());
    175                         } catch (IOException ex) {
    176                                 help.setText(tr("Error while loading page {0}",url));
    177                         }
    178                 }
    179                 else
    180                 {
    181                         try {
    182                                 help.read(new StringReader(reader.read(langurl)), help.getEditorKit().createDefaultDocument());
    183                                 String message = help.getText();
    184                                 String le = "http://josm-extern." + langurl.substring(7);
    185                                 if(message.indexOf("Describe &quot;") >= 0 && message.indexOf(le) >= 0)
    186                                         throw new IOException();
    187                                 this.url = langurl;
    188                         } catch (IOException e) {
    189                                 this.url = url;
    190                                 try {
    191                                         help.read(new StringReader(reader.read(url)), help.getEditorKit().createDefaultDocument());
    192                                 } catch (IOException ex) {
    193                                         help.setText(tr("Error while loading page {0}",url));
    194                                 }
    195                         }
    196                 }
    197                 helpBrowser.setVisible(true);
    198         }
    199 
    200         /**
    201         * Closes the help window
    202         */
    203         public void closeHelp() {
    204                 String bounds = helpBrowser.getX()+","+helpBrowser.getY()+","+helpBrowser.getWidth()+","+helpBrowser.getHeight();
    205                 Main.pref.put("help.window.bounds", bounds);
    206                 helpBrowser.setVisible(false);
    207         }
    208 
    209         private void createButton(JPanel buttons, String name) {
    210                 JButton b = new JButton(tr(name));
    211                 b.setActionCommand(name);
    212                 b.addActionListener(this);
    213                 buttons.add(b);
    214         }
     101    }
     102
     103    public void actionPerformed(ActionEvent e) {
     104        if (tr("Open in Browser").equals(e.getActionCommand())) {
     105            OpenBrowser.displayUrl(url);
     106        } else if (tr("Edit").equals(e.getActionCommand())) {
     107            if (!url.startsWith(baseurl)) {
     108                JOptionPane.showMessageDialog(Main.parent, tr("Can only edit help pages from JOSM Online Help"));
     109                return;
     110            }
     111            OpenBrowser.displayUrl(url+"?action=edit");
     112        } else if (tr("Reload").equals(e.getActionCommand())) {
     113            setHelpUrl(url);
     114        } else if (e.getActionCommand() == null) {
     115            String topic = null;
     116            Point mouse = Main.parent.getMousePosition();
     117            if (mouse != null)
     118                topic = contextSensitiveHelp(SwingUtilities.getDeepestComponentAt(Main.parent, mouse.x, mouse.y));
     119            if (topic == null) {
     120                helpBrowser.setVisible(false);
     121                setHelpUrl(baseurl+"/wiki/Help");
     122            } else
     123                help(topic);
     124        } else {
     125            helpBrowser.setVisible(false);
     126            setHelpUrl(baseurl+"/wiki/Help");
     127        }
     128    }
     129
     130    /**
     131    * @return The topic of the help. <code>null</code> for "don't know"
     132    */
     133    private String contextSensitiveHelp(Object c) {
     134        if (c instanceof Helpful)
     135            return ((Helpful)c).helpTopic();
     136        if (c instanceof JMenu)
     137            return "Menu/"+((JMenu)c).getText();
     138        if (c instanceof AbstractButton) {
     139            AbstractButton b = (AbstractButton)c;
     140            if (b.getClientProperty("help") != null)
     141                return (String)b.getClientProperty("help");
     142            return contextSensitiveHelp(((AbstractButton)c).getAction());
     143        }
     144        if (c instanceof Action)
     145            return (String)((Action)c).getValue("help");
     146        if (c instanceof Component)
     147            return contextSensitiveHelp(((Component)c).getParent());
     148        return null;
     149    }
     150
     151    /**
     152    * Displays the help (or browse on the already open help) on the online page
     153    * with the given help topic. Use this for larger help descriptions.
     154    */
     155    public void help(String topic) {
     156        helpBrowser.setVisible(false);
     157        setHelpUrl(baseurl+"/wiki/Help/"+topic);
     158    }
     159
     160    /**
     161    * Set the content of the help window to a specific text (in html format)
     162    * @param url The url this content is the representation of
     163    */
     164    public void setHelpUrl(String url) {
     165        int i = url.lastIndexOf("/")+1;
     166        String title = url.substring(i);
     167        if(!title.startsWith(languageCode))
     168            title = languageCode + title;
     169        String langurl = url.substring(0, i) + title;
     170        if(langurl.equals(this.url) || langurl.equals(url))
     171        {
     172            this.url = url;
     173            try {
     174                help.read(new StringReader(reader.read(url)), help.getEditorKit().createDefaultDocument());
     175            } catch (IOException ex) {
     176                help.setText(tr("Error while loading page {0}",url));
     177            }
     178        }
     179        else
     180        {
     181            try {
     182                help.read(new StringReader(reader.read(langurl)), help.getEditorKit().createDefaultDocument());
     183                String message = help.getText();
     184                String le = "http://josm-extern." + langurl.substring(7);
     185                if(message.indexOf("Describe &quot;") >= 0 && message.indexOf(le) >= 0)
     186                    throw new IOException();
     187                this.url = langurl;
     188            } catch (IOException e) {
     189                this.url = url;
     190                try {
     191                    help.read(new StringReader(reader.read(url)), help.getEditorKit().createDefaultDocument());
     192                } catch (IOException ex) {
     193                    help.setText(tr("Error while loading page {0}",url));
     194                }
     195            }
     196        }
     197        helpBrowser.setVisible(true);
     198    }
     199
     200    /**
     201    * Closes the help window
     202    */
     203    public void closeHelp() {
     204        String bounds = helpBrowser.getX()+","+helpBrowser.getY()+","+helpBrowser.getWidth()+","+helpBrowser.getHeight();
     205        Main.pref.put("help.window.bounds", bounds);
     206        helpBrowser.setVisible(false);
     207    }
     208
     209    private void createButton(JPanel buttons, String name) {
     210        JButton b = new JButton(tr(name));
     211        b.setActionCommand(name);
     212        b.addActionListener(this);
     213        buttons.add(b);
     214    }
    215215}
  • trunk/src/org/openstreetmap/josm/actions/HistoryInfoAction.java

    r1084 r1169  
    1818public class HistoryInfoAction extends JosmAction {
    1919
    20         public HistoryInfoAction() {
    21                 super(tr("OSM History Information"), "about",tr("Display history information about OSM ways or nodes."),
    22                 Shortcut.registerShortcut("core:history", tr("OSM History Information"), KeyEvent.VK_H, Shortcut.GROUP_HOTKEY), true);
    23         }
     20    public HistoryInfoAction() {
     21        super(tr("OSM History Information"), "about",tr("Display history information about OSM ways or nodes."),
     22        Shortcut.registerShortcut("core:history", tr("OSM History Information"), KeyEvent.VK_H, Shortcut.GROUP_HOTKEY), true);
     23    }
    2424
    25         public void actionPerformed(ActionEvent e) {
     25    public void actionPerformed(ActionEvent e) {
    2626                new Visitor() {
    2727                        public void visit(Node n) {
    28                                 OpenBrowser.displayUrl("http://www.openstreetmap.org/browse/node/" + n.id + "/history");
    29                         }
     28                OpenBrowser.displayUrl("http://www.openstreetmap.org/browse/node/" + n.id + "/history");
     29            }
    3030
    3131                        public void visit(Way w) {
     
    4343                }.visitAll();
    4444
    45         }
     45    }
    4646
    4747}
  • trunk/src/org/openstreetmap/josm/actions/JoinNodeWayAction.java

    r1084 r1169  
    2626
    2727public class JoinNodeWayAction extends JosmAction {
    28         public JoinNodeWayAction() {
    29             super(tr("Join node to way"), "joinnodeway", tr("Join a node into the nearest way segments"),
    30                         Shortcut.registerShortcut("tools:joinnodeway", tr("Tool: {0}", tr("Join node to way")), KeyEvent.VK_J, Shortcut.GROUP_EDIT), true);
    31         }
     28    public JoinNodeWayAction() {
     29        super(tr("Join node to way"), "joinnodeway", tr("Join a node into the nearest way segments"),
     30            Shortcut.registerShortcut("tools:joinnodeway", tr("Tool: {0}", tr("Join node to way")), KeyEvent.VK_J, Shortcut.GROUP_EDIT), true);
     31    }
    3232
    33         public void actionPerformed(ActionEvent e) {
    34                 Collection<OsmPrimitive> sel = Main.ds.getSelected();
    35                 if (sel.size() != 1 || !(sel.iterator().next() instanceof Node)) return;
    36                 Node node = (Node) sel.iterator().next();
     33    public void actionPerformed(ActionEvent e) {
     34        Collection<OsmPrimitive> sel = Main.ds.getSelected();
     35        if (sel.size() != 1 || !(sel.iterator().next() instanceof Node)) return;
     36        Node node = (Node) sel.iterator().next();
    3737
    38                 List<WaySegment> wss = Main.map.mapView.getNearestWaySegments(
    39                         Main.map.mapView.getPoint(node.eastNorth));
    40                 HashMap<Way, List<Integer>> insertPoints = new HashMap<Way, List<Integer>>();
    41                 for (WaySegment ws : wss) {
    42                         List<Integer> is;
    43                         if (insertPoints.containsKey(ws.way)) {
    44                                 is = insertPoints.get(ws.way);
    45                         } else {
    46                                 is = new ArrayList<Integer>();
    47                                 insertPoints.put(ws.way, is);
    48                         }
     38        List<WaySegment> wss = Main.map.mapView.getNearestWaySegments(
     39            Main.map.mapView.getPoint(node.eastNorth));
     40        HashMap<Way, List<Integer>> insertPoints = new HashMap<Way, List<Integer>>();
     41        for (WaySegment ws : wss) {
     42            List<Integer> is;
     43            if (insertPoints.containsKey(ws.way)) {
     44                is = insertPoints.get(ws.way);
     45            } else {
     46                is = new ArrayList<Integer>();
     47                insertPoints.put(ws.way, is);
     48            }
    4949
    50                         if (ws.way.nodes.get(ws.lowerIndex) != node
    51                                         && ws.way.nodes.get(ws.lowerIndex+1) != node) {
    52                                 is.add(ws.lowerIndex);
    53                         }
    54                 }
     50            if (ws.way.nodes.get(ws.lowerIndex) != node
     51                    && ws.way.nodes.get(ws.lowerIndex+1) != node) {
     52                is.add(ws.lowerIndex);
     53            }
     54        }
    5555
    56                 Collection<Command> cmds = new LinkedList<Command>();
    57                 for (Map.Entry<Way, List<Integer>> insertPoint : insertPoints.entrySet()) {
    58                         Way w = insertPoint.getKey();
    59                         Way wnew = new Way(w);
    60                         List<Integer> is = insertPoint.getValue();
    61                         pruneSuccsAndReverse(is);
    62                         for (int i : is) wnew.nodes.add(i+1, node);
    63                         cmds.add(new ChangeCommand(w, wnew));
    64                 }
     56        Collection<Command> cmds = new LinkedList<Command>();
     57        for (Map.Entry<Way, List<Integer>> insertPoint : insertPoints.entrySet()) {
     58            Way w = insertPoint.getKey();
     59            Way wnew = new Way(w);
     60            List<Integer> is = insertPoint.getValue();
     61            pruneSuccsAndReverse(is);
     62            for (int i : is) wnew.nodes.add(i+1, node);
     63            cmds.add(new ChangeCommand(w, wnew));
     64        }
    6565
    66                 Main.main.undoRedo.add(new SequenceCommand(tr("Join Node and Line"), cmds));
    67                 Main.map.repaint();
    68         }
     66        Main.main.undoRedo.add(new SequenceCommand(tr("Join Node and Line"), cmds));
     67        Main.map.repaint();
     68    }
    6969
    70         private static void pruneSuccsAndReverse(List<Integer> is) {
    71                 //if (is.size() < 2) return;
     70    private static void pruneSuccsAndReverse(List<Integer> is) {
     71        //if (is.size() < 2) return;
    7272
    73                 HashSet<Integer> is2 = new HashSet<Integer>();
    74                 for (int i : is) {
    75                         if (!is2.contains(i - 1) && !is2.contains(i + 1)) {
    76                                 is2.add(i);
    77                         }
    78                 }
    79                 is.clear();
    80                 is.addAll(is2);
    81                 Collections.sort(is);
    82                 Collections.reverse(is);
    83         }
     73        HashSet<Integer> is2 = new HashSet<Integer>();
     74        for (int i : is) {
     75            if (!is2.contains(i - 1) && !is2.contains(i + 1)) {
     76                is2.add(i);
     77            }
     78        }
     79        is.clear();
     80        is.addAll(is2);
     81        Collections.sort(is);
     82        Collections.reverse(is);
     83    }
    8484}
  • trunk/src/org/openstreetmap/josm/actions/JosmAction.java

    r1084 r1169  
    2525abstract public class JosmAction extends AbstractAction implements Destroyable {
    2626
    27         @Deprecated
    28         public KeyStroke shortcut;
    29         protected Shortcut sc;
     27    @Deprecated
     28    public KeyStroke shortcut;
     29    protected Shortcut sc;
    3030
    31         public Shortcut getShortcut() {
    32                 if (sc == null) {
    33                         sc = Shortcut.registerShortcut("core:none", "No Shortcut", 0, Shortcut.GROUP_NONE);
    34                         sc.setAutomatic(); // as this shortcut is shared by all action that don't want to have a shortcut,
    35                                            // we shouldn't allow the user to change it...
    36                 }
    37                 return sc;
    38         }
     31    public Shortcut getShortcut() {
     32        if (sc == null) {
     33            sc = Shortcut.registerShortcut("core:none", "No Shortcut", 0, Shortcut.GROUP_NONE);
     34            sc.setAutomatic(); // as this shortcut is shared by all action that don't want to have a shortcut,
     35                               // we shouldn't allow the user to change it...
     36        }
     37        return sc;
     38    }
    3939
    40         @Deprecated
    41         public JosmAction(String name, String iconName, String tooltip, int shortcut, int modifier, boolean register) {
    42                 super(name, iconName == null ? null : ImageProvider.get(iconName));
    43                 setHelpId();
    44                 if (shortcut != 0) {
    45                         int group = Shortcut.GROUP_LAYER; //GROUP_NONE;
    46                         if (((modifier & InputEvent.CTRL_MASK) != 0) || ((modifier & InputEvent.CTRL_DOWN_MASK) != 0)) {
    47                                 group = Shortcut.GROUP_MENU;
    48                         } else if (modifier == 0) {
    49                                 group = Shortcut.GROUP_EDIT;
    50                         }
    51                         sc = Shortcut.registerShortcut("auto:"+name, name, shortcut, group);
    52                         this.shortcut = sc.getKeyStroke();
    53                         Main.contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(sc.getKeyStroke(), name);
    54                         Main.contentPane.getActionMap().put(name, this);
    55                 }
    56                 putValue(SHORT_DESCRIPTION, Main.platform.makeTooltip(tooltip, sc));
    57                 putValue("toolbar", iconName);
     40    @Deprecated
     41    public JosmAction(String name, String iconName, String tooltip, int shortcut, int modifier, boolean register) {
     42        super(name, iconName == null ? null : ImageProvider.get(iconName));
     43        setHelpId();
     44        if (shortcut != 0) {
     45            int group = Shortcut.GROUP_LAYER; //GROUP_NONE;
     46            if (((modifier & InputEvent.CTRL_MASK) != 0) || ((modifier & InputEvent.CTRL_DOWN_MASK) != 0)) {
     47                group = Shortcut.GROUP_MENU;
     48            } else if (modifier == 0) {
     49                group = Shortcut.GROUP_EDIT;
     50            }
     51            sc = Shortcut.registerShortcut("auto:"+name, name, shortcut, group);
     52            this.shortcut = sc.getKeyStroke();
     53            Main.contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(sc.getKeyStroke(), name);
     54            Main.contentPane.getActionMap().put(name, this);
     55        }
     56        putValue(SHORT_DESCRIPTION, Main.platform.makeTooltip(tooltip, sc));
     57        putValue("toolbar", iconName);
    5858    if (register)
    59         Main.toolbar.register(this);
    60         }
     59        Main.toolbar.register(this);
     60    }
    6161
    62         /**
    63         * The new super for all actions.
    64         *
    65         * Use this super constructor to setup your action. It takes 5 parameters:
    66         *
    67         * name - the action's text as displayed on the menu (if it is added to a menu)
    68         * iconName - the filename of the icon to use
    69         * tooltip - a longer description of the action that will be displayed in the tooltip. Please note
    70         *           that html is not supported for menu action on some platforms
    71         * shortcut - a ready-created shortcut object or null if you don't want a shortcut. But you always
    72         *            do want a shortcut, remember you can alway register it with group=none, so you
    73         *            won't be assigned a shurtcut unless the user configures one. If you pass null here,
    74         *            the user CANNOT configure a shortcut for your action.
    75         * register - register this action for the toolbar preferences?
    76         */
    77         public JosmAction(String name, String iconName, String tooltip, Shortcut shortcut, boolean register) {
    78                 super(name, iconName == null ? null : ImageProvider.get(iconName));
    79                 setHelpId();
    80                 sc = shortcut;
    81                 if (sc != null) {
    82                         this.shortcut = sc.getKeyStroke();
    83                         Main.contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(sc.getKeyStroke(), name);
    84                         Main.contentPane.getActionMap().put(name, this);
    85                 }
    86                 putValue(SHORT_DESCRIPTION, Main.platform.makeTooltip(tooltip, sc));
    87                 putValue("toolbar", iconName);
     62    /**
     63    * The new super for all actions.
     64    *
     65    * Use this super constructor to setup your action. It takes 5 parameters:
     66    *
     67    * name - the action's text as displayed on the menu (if it is added to a menu)
     68    * iconName - the filename of the icon to use
     69    * tooltip - a longer description of the action that will be displayed in the tooltip. Please note
     70    *           that html is not supported for menu action on some platforms
     71    * shortcut - a ready-created shortcut object or null if you don't want a shortcut. But you always
     72    *            do want a shortcut, remember you can alway register it with group=none, so you
     73    *            won't be assigned a shurtcut unless the user configures one. If you pass null here,
     74    *            the user CANNOT configure a shortcut for your action.
     75    * register - register this action for the toolbar preferences?
     76    */
     77    public JosmAction(String name, String iconName, String tooltip, Shortcut shortcut, boolean register) {
     78        super(name, iconName == null ? null : ImageProvider.get(iconName));
     79        setHelpId();
     80        sc = shortcut;
     81        if (sc != null) {
     82            this.shortcut = sc.getKeyStroke();
     83            Main.contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(sc.getKeyStroke(), name);
     84            Main.contentPane.getActionMap().put(name, this);
     85        }
     86        putValue(SHORT_DESCRIPTION, Main.platform.makeTooltip(tooltip, sc));
     87        putValue("toolbar", iconName);
    8888    if (register)
    89         Main.toolbar.register(this);
    90         }
     89        Main.toolbar.register(this);
     90    }
    9191
    92         public void destroy() {
    93                 if (shortcut != null) {
    94                         Main.contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).remove(sc.getKeyStroke());
    95                         Main.contentPane.getActionMap().remove(sc.getKeyStroke());
    96                 }
    97         }
     92    public void destroy() {
     93        if (shortcut != null) {
     94            Main.contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).remove(sc.getKeyStroke());
     95            Main.contentPane.getActionMap().remove(sc.getKeyStroke());
     96        }
     97    }
    9898
    99         public JosmAction() {
    100                 setHelpId();
    101         }
     99    public JosmAction() {
     100        setHelpId();
     101    }
    102102
    103         /**
    104         * needs to be overridden to be useful
    105         */
    106         public void pasteBufferChanged(DataSet newPasteBuffer) {
    107                 return;
    108         }
     103    /**
     104    * needs to be overridden to be useful
     105    */
     106    public void pasteBufferChanged(DataSet newPasteBuffer) {
     107        return;
     108    }
    109109
    110         /**
    111         * needs to be overridden to be useful
    112         */
    113         public void addListener(JosmAction a) {
    114                 return;
    115         }
     110    /**
     111    * needs to be overridden to be useful
     112    */
     113    public void addListener(JosmAction a) {
     114        return;
     115    }
    116116
    117         private void setHelpId() {
    118                 String helpId = "Action/"+getClass().getName().substring(getClass().getName().lastIndexOf('.')+1);
    119                 if (helpId.endsWith("Action"))
    120                         helpId = helpId.substring(0, helpId.length()-6);
    121                 putValue("help", helpId);
    122         }
     117    private void setHelpId() {
     118        String helpId = "Action/"+getClass().getName().substring(getClass().getName().lastIndexOf('.')+1);
     119        if (helpId.endsWith("Action"))
     120            helpId = helpId.substring(0, helpId.length()-6);
     121        putValue("help", helpId);
     122    }
    123123}
  • trunk/src/org/openstreetmap/josm/actions/MergeNodesAction.java

    r1084 r1169  
    5252public class MergeNodesAction extends JosmAction implements SelectionChangedListener {
    5353
    54         public MergeNodesAction() {
    55                 super(tr("Merge Nodes"), "mergenodes", tr("Merge nodes into the oldest one."),
    56                 Shortcut.registerShortcut("tools:mergenodes", tr("Tool: {0}", tr("Merge Nodes")), KeyEvent.VK_M, Shortcut.GROUP_EDIT), true);
    57                 DataSet.selListeners.add(this);
    58         }
    59 
    60         public void actionPerformed(ActionEvent event) {
    61                 Collection<OsmPrimitive> selection = Main.ds.getSelected();
    62                 LinkedList<Node> selectedNodes = new LinkedList<Node>();
    63 
    64                 // the selection check should stop this procedure starting if
    65                 // nothing but node are selected - otherwise we don't care
    66                 // anyway as long as we have at least two nodes
    67                 for (OsmPrimitive osm : selection)
    68                         if (osm instanceof Node)
    69                                 selectedNodes.add((Node)osm);
    70 
    71                 if (selectedNodes.size() < 2) {
    72                         JOptionPane.showMessageDialog(Main.parent, tr("Please select at least two nodes to merge."));
    73                         return;
    74                 }
    75 
    76                 // Find which node to merge into (i.e. which one will be left)
    77                 // - this should be combined from two things:
    78                 //   1. It will be the first node in the list that has a
    79                 //      positive ID number, OR the first node.
    80                 //   2. It will be at the position of the first node in the
    81                 //      list.
    82                 //
    83                 // *However* - there is the problem that the selection list is
    84                 // _not_ in the order that the nodes were clicked on, meaning
    85                 // that the user doesn't know which node will be chosen (so
    86                 // (2) is not implemented yet.)  :-(
    87                 Node useNode = null;
    88                 for (Node n: selectedNodes) {
    89                         if (n.id > 0) {
    90                                 useNode = n;
    91                                 break;
    92                         }
    93                 }
    94                 if (useNode == null)
    95                         useNode = selectedNodes.iterator().next();
    96 
    97                 mergeNodes(selectedNodes, useNode);
    98         }
    99 
    100         /**
    101         * really do the merging - returns the node that is left
    102         */
    103         public static Node mergeNodes(LinkedList<Node> allNodes, Node dest) {
    104                 Node newNode = new Node(dest);
    105 
    106                 // Check whether all ways have identical relationship membership. More
    107                 // specifically: If one of the selected ways is a member of relation X
    108                 // in role Y, then all selected ways must be members of X in role Y.
    109 
    110                 // FIXME: In a later revision, we should display some sort of conflict
    111                 // dialog like we do for tags, to let the user choose which relations
    112                 // should be kept.
    113 
    114                 // Step 1, iterate over all relations and figure out which of our
    115                 // selected ways are members of a relation.
    116                 HashMap<Pair<Relation,String>, HashSet<Node>> backlinks =
    117                         new HashMap<Pair<Relation,String>, HashSet<Node>>();
    118                 HashSet<Relation> relationsUsingNodes = new HashSet<Relation>();
    119                 for (Relation r : Main.ds.relations) {
    120                         if (r.deleted || r.incomplete) continue;
    121                         for (RelationMember rm : r.members) {
    122                                 if (rm.member instanceof Node) {
    123                                         for (Node n : allNodes) {
    124                                                 if (rm.member == n) {
    125                                                         Pair<Relation,String> pair = new Pair<Relation,String>(r, rm.role);
    126                                                         HashSet<Node> nodelinks = new HashSet<Node>();
    127                                                         if (backlinks.containsKey(pair)) {
    128                                                                 nodelinks = backlinks.get(pair);
    129                                                         } else {
    130                                                                 nodelinks = new HashSet<Node>();
    131                                                                 backlinks.put(pair, nodelinks);
    132                                                         }
    133                                                         nodelinks.add(n);
    134 
    135                                                         // this is just a cache for later use
    136                                                         relationsUsingNodes.add(r);
    137                                                 }
    138                                         }
    139                                 }
    140                         }
    141                 }
    142 
    143                 // Complain to the user if the ways don't have equal memberships.
    144                 for (HashSet<Node> nodelinks : backlinks.values()) {
    145                         if (!nodelinks.containsAll(allNodes)) {
    146                                 int option = JOptionPane.showConfirmDialog(Main.parent,
    147                                         tr("The selected nodes have differing relation memberships.  "
    148                                                 + "Do you still want to merge them?"),
    149                                         tr("Merge nodes with different memberships?"),
    150                                         JOptionPane.YES_NO_OPTION);
    151                                 if (option == JOptionPane.YES_OPTION)
    152                                         break;
    153                                 return null;
    154                         }
    155                 }
    156 
    157                 // collect properties for later conflict resolving
    158                 Map<String, Set<String>> props = new TreeMap<String, Set<String>>();
    159                 for (Node n : allNodes) {
    160                         for (Entry<String,String> e : n.entrySet()) {
    161                                 if (!props.containsKey(e.getKey()))
    162                                         props.put(e.getKey(), new TreeSet<String>());
    163                                 props.get(e.getKey()).add(e.getValue());
    164                         }
    165                 }
    166 
    167                 // display conflict dialog
    168                 Map<String, JComboBox> components = new HashMap<String, JComboBox>();
    169                 JPanel p = new JPanel(new GridBagLayout());
    170                 for (Entry<String, Set<String>> e : props.entrySet()) {
    171                         if (TigerUtils.isTigerTag(e.getKey())) {
    172                                 String combined = TigerUtils.combineTags(e.getKey(), e.getValue());
    173                                 newNode.put(e.getKey(), combined);
    174                         } else if (e.getValue().size() > 1) {
    175                                 if("created_by".equals(e.getKey()))
    176                                 {
    177                                         newNode.put("created_by", "JOSM");
    178                                 }
    179                                 else
    180                                 {
    181                                         JComboBox c = new JComboBox(e.getValue().toArray());
    182                                         c.setEditable(true);
    183                                         p.add(new JLabel(e.getKey()), GBC.std());
    184                                         p.add(Box.createHorizontalStrut(10), GBC.std());
    185                                         p.add(c, GBC.eol());
    186                                         components.put(e.getKey(), c);
    187                                 }
    188                         } else
    189                                 newNode.put(e.getKey(), e.getValue().iterator().next());
    190                 }
    191 
    192                 if (!components.isEmpty()) {
    193                         int answer = JOptionPane.showConfirmDialog(Main.parent, p, tr("Enter values for all conflicts."), JOptionPane.OK_CANCEL_OPTION);
    194                         if (answer != JOptionPane.OK_OPTION)
    195                                 return null;
    196                         for (Entry<String, JComboBox> e : components.entrySet())
    197                                 newNode.put(e.getKey(), e.getValue().getEditor().getItem().toString());
    198                 }
    199 
    200                 LinkedList<Command> cmds = new LinkedList<Command>();
    201                 cmds.add(new ChangeCommand(dest, newNode));
    202 
    203                 Collection<OsmPrimitive> del = new HashSet<OsmPrimitive>();
    204 
    205                 for (Way w : Main.ds.ways) {
    206                         if (w.deleted || w.incomplete || w.nodes.size() < 1) continue;
    207                         boolean modify = false;
    208                         for (Node sn : allNodes) {
    209                                 if (sn == dest) continue;
    210                                 if (w.nodes.contains(sn)) {
    211                                         modify = true;
    212                                 }
    213                         }
    214                         if (!modify) continue;
    215                         // OK - this way contains one or more nodes to change
    216                         ArrayList<Node> nn = new ArrayList<Node>();
    217                         Node lastNode = null;
    218                         for (int i = 0; i < w.nodes.size(); i++) {
    219                                 Node pushNode = w.nodes.get(i);
    220                                 if (allNodes.contains(pushNode)) {
    221                                         pushNode = dest;
    222                                 }
    223                                 if (pushNode != lastNode) {
    224                                         nn.add(pushNode);
    225                                 }
    226                                 lastNode = pushNode;
    227                         }
    228                         if (nn.size() < 2) {
    229                                 CollectBackReferencesVisitor backRefs =
    230                                         new CollectBackReferencesVisitor(Main.ds, false);
    231                                 w.visit(backRefs);
    232                                 if (!backRefs.data.isEmpty()) {
    233                                         JOptionPane.showMessageDialog(Main.parent,
    234                                                 tr("Cannot merge nodes: " +
    235                                                         "Would have to delete a way that is still used."));
    236                                         return null;
    237                                 }
    238                                 del.add(w);
    239                         } else {
    240                                 Way newWay = new Way(w);
    241                                 newWay.nodes.clear();
    242                                 newWay.nodes.addAll(nn);
    243                                 cmds.add(new ChangeCommand(w, newWay));
    244                         }
    245                 }
    246 
    247                 // delete any merged nodes
    248                 del.addAll(allNodes);
    249                 del.remove(dest);
    250                 if (!del.isEmpty()) cmds.add(new DeleteCommand(del));
    251 
    252                 // modify all relations containing the now-deleted nodes
    253                 for (Relation r : relationsUsingNodes) {
    254                         Relation newRel = new Relation(r);
    255                         newRel.members.clear();
    256                         HashSet<String> rolesToReAdd = new HashSet<String>();
    257                         for (RelationMember rm : r.members) {
    258                                 // Don't copy the member if it points to one of our nodes,
    259                                 // just keep a note to re-add it later on.
    260                                 if (allNodes.contains(rm.member)) {
    261                                         rolesToReAdd.add(rm.role);
    262                                 } else {
    263                                         newRel.members.add(rm);
    264                                 }
    265                         }
    266                         for (String role : rolesToReAdd) {
    267                                 newRel.members.add(new RelationMember(role, dest));
    268                         }
    269                         cmds.add(new ChangeCommand(r, newRel));
    270                 }
    271 
    272                 Main.main.undoRedo.add(new SequenceCommand(tr("Merge {0} nodes", allNodes.size()), cmds));
    273                 Main.ds.setSelected(dest);
    274 
    275                 return dest;
    276         }
    277 
    278 
    279         /**
    280         * Enable the "Merge Nodes" menu option if more then one node is selected
    281         */
    282         public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) {
    283                 boolean ok = true;
    284                 if (newSelection.size() < 2) {
    285                         setEnabled(false);
    286                         return;
    287                 }
    288                 for (OsmPrimitive osm : newSelection) {
    289                         if (!(osm instanceof Node)) {
    290                                 ok = false;
    291                                 break;
    292                         }
    293                 }
    294                 setEnabled(ok);
    295         }
     54    public MergeNodesAction() {
     55        super(tr("Merge Nodes"), "mergenodes", tr("Merge nodes into the oldest one."),
     56        Shortcut.registerShortcut("tools:mergenodes", tr("Tool: {0}", tr("Merge Nodes")), KeyEvent.VK_M, Shortcut.GROUP_EDIT), true);
     57        DataSet.selListeners.add(this);
     58    }
     59
     60    public void actionPerformed(ActionEvent event) {
     61        Collection<OsmPrimitive> selection = Main.ds.getSelected();
     62        LinkedList<Node> selectedNodes = new LinkedList<Node>();
     63
     64        // the selection check should stop this procedure starting if
     65        // nothing but node are selected - otherwise we don't care
     66        // anyway as long as we have at least two nodes
     67        for (OsmPrimitive osm : selection)
     68            if (osm instanceof Node)
     69                selectedNodes.add((Node)osm);
     70
     71        if (selectedNodes.size() < 2) {
     72            JOptionPane.showMessageDialog(Main.parent, tr("Please select at least two nodes to merge."));
     73            return;
     74        }
     75
     76        // Find which node to merge into (i.e. which one will be left)
     77        // - this should be combined from two things:
     78        //   1. It will be the first node in the list that has a
     79        //      positive ID number, OR the first node.
     80        //   2. It will be at the position of the first node in the
     81        //      list.
     82        //
     83        // *However* - there is the problem that the selection list is
     84        // _not_ in the order that the nodes were clicked on, meaning
     85        // that the user doesn't know which node will be chosen (so
     86        // (2) is not implemented yet.)  :-(
     87        Node useNode = null;
     88        for (Node n: selectedNodes) {
     89            if (n.id > 0) {
     90                useNode = n;
     91                break;
     92            }
     93        }
     94        if (useNode == null)
     95            useNode = selectedNodes.iterator().next();
     96
     97        mergeNodes(selectedNodes, useNode);
     98    }
     99
     100    /**
     101    * really do the merging - returns the node that is left
     102    */
     103    public static Node mergeNodes(LinkedList<Node> allNodes, Node dest) {
     104        Node newNode = new Node(dest);
     105
     106        // Check whether all ways have identical relationship membership. More
     107        // specifically: If one of the selected ways is a member of relation X
     108        // in role Y, then all selected ways must be members of X in role Y.
     109
     110        // FIXME: In a later revision, we should display some sort of conflict
     111        // dialog like we do for tags, to let the user choose which relations
     112        // should be kept.
     113
     114        // Step 1, iterate over all relations and figure out which of our
     115        // selected ways are members of a relation.
     116        HashMap<Pair<Relation,String>, HashSet<Node>> backlinks =
     117            new HashMap<Pair<Relation,String>, HashSet<Node>>();
     118        HashSet<Relation> relationsUsingNodes = new HashSet<Relation>();
     119        for (Relation r : Main.ds.relations) {
     120            if (r.deleted || r.incomplete) continue;
     121            for (RelationMember rm : r.members) {
     122                if (rm.member instanceof Node) {
     123                    for (Node n : allNodes) {
     124                        if (rm.member == n) {
     125                            Pair<Relation,String> pair = new Pair<Relation,String>(r, rm.role);
     126                            HashSet<Node> nodelinks = new HashSet<Node>();
     127                            if (backlinks.containsKey(pair)) {
     128                                nodelinks = backlinks.get(pair);
     129                            } else {
     130                                nodelinks = new HashSet<Node>();
     131                                backlinks.put(pair, nodelinks);
     132                            }
     133                            nodelinks.add(n);
     134
     135                            // this is just a cache for later use
     136                            relationsUsingNodes.add(r);
     137                        }
     138                    }
     139                }
     140            }
     141        }
     142
     143        // Complain to the user if the ways don't have equal memberships.
     144        for (HashSet<Node> nodelinks : backlinks.values()) {
     145            if (!nodelinks.containsAll(allNodes)) {
     146                int option = JOptionPane.showConfirmDialog(Main.parent,
     147                    tr("The selected nodes have differing relation memberships.  "
     148                        + "Do you still want to merge them?"),
     149                    tr("Merge nodes with different memberships?"),
     150                    JOptionPane.YES_NO_OPTION);
     151                if (option == JOptionPane.YES_OPTION)
     152                    break;
     153                return null;
     154            }
     155        }
     156
     157        // collect properties for later conflict resolving
     158        Map<String, Set<String>> props = new TreeMap<String, Set<String>>();
     159        for (Node n : allNodes) {
     160            for (Entry<String,String> e : n.entrySet()) {
     161                if (!props.containsKey(e.getKey()))
     162                    props.put(e.getKey(), new TreeSet<String>());
     163                props.get(e.getKey()).add(e.getValue());
     164            }
     165        }
     166
     167        // display conflict dialog
     168        Map<String, JComboBox> components = new HashMap<String, JComboBox>();
     169        JPanel p = new JPanel(new GridBagLayout());
     170        for (Entry<String, Set<String>> e : props.entrySet()) {
     171            if (TigerUtils.isTigerTag(e.getKey())) {
     172                String combined = TigerUtils.combineTags(e.getKey(), e.getValue());
     173                newNode.put(e.getKey(), combined);
     174            } else if (e.getValue().size() > 1) {
     175                if("created_by".equals(e.getKey()))
     176                {
     177                    newNode.put("created_by", "JOSM");
     178                }
     179                else
     180                {
     181                    JComboBox c = new JComboBox(e.getValue().toArray());
     182                    c.setEditable(true);
     183                    p.add(new JLabel(e.getKey()), GBC.std());
     184                    p.add(Box.createHorizontalStrut(10), GBC.std());
     185                    p.add(c, GBC.eol());
     186                    components.put(e.getKey(), c);
     187                }
     188            } else
     189                newNode.put(e.getKey(), e.getValue().iterator().next());
     190        }
     191
     192        if (!components.isEmpty()) {
     193            int answer = JOptionPane.showConfirmDialog(Main.parent, p, tr("Enter values for all conflicts."), JOptionPane.OK_CANCEL_OPTION);
     194            if (answer != JOptionPane.OK_OPTION)
     195                return null;
     196            for (Entry<String, JComboBox> e : components.entrySet())
     197                newNode.put(e.getKey(), e.getValue().getEditor().getItem().toString());
     198        }
     199
     200        LinkedList<Command> cmds = new LinkedList<Command>();
     201        cmds.add(new ChangeCommand(dest, newNode));
     202
     203        Collection<OsmPrimitive> del = new HashSet<OsmPrimitive>();
     204
     205        for (Way w : Main.ds.ways) {
     206            if (w.deleted || w.incomplete || w.nodes.size() < 1) continue;
     207            boolean modify = false;
     208            for (Node sn : allNodes) {
     209                if (sn == dest) continue;
     210                if (w.nodes.contains(sn)) {
     211                    modify = true;
     212                }
     213            }
     214            if (!modify) continue;
     215            // OK - this way contains one or more nodes to change
     216            ArrayList<Node> nn = new ArrayList<Node>();
     217            Node lastNode = null;
     218            for (int i = 0; i < w.nodes.size(); i++) {
     219                Node pushNode = w.nodes.get(i);
     220                if (allNodes.contains(pushNode)) {
     221                    pushNode = dest;
     222                }
     223                if (pushNode != lastNode) {
     224                    nn.add(pushNode);
     225                }
     226                lastNode = pushNode;
     227            }
     228            if (nn.size() < 2) {
     229                CollectBackReferencesVisitor backRefs =
     230                    new CollectBackReferencesVisitor(Main.ds, false);
     231                w.visit(backRefs);
     232                if (!backRefs.data.isEmpty()) {
     233                    JOptionPane.showMessageDialog(Main.parent,
     234                        tr("Cannot merge nodes: " +
     235                            "Would have to delete a way that is still used."));
     236                    return null;
     237                }
     238                del.add(w);
     239            } else {
     240                Way newWay = new Way(w);
     241                newWay.nodes.clear();
     242                newWay.nodes.addAll(nn);
     243                cmds.add(new ChangeCommand(w, newWay));
     244            }
     245        }
     246
     247        // delete any merged nodes
     248        del.addAll(allNodes);
     249        del.remove(dest);
     250        if (!del.isEmpty()) cmds.add(new DeleteCommand(del));
     251
     252        // modify all relations containing the now-deleted nodes
     253        for (Relation r : relationsUsingNodes) {
     254            Relation newRel = new Relation(r);
     255            newRel.members.clear();
     256            HashSet<String> rolesToReAdd = new HashSet<String>();
     257            for (RelationMember rm : r.members) {
     258                // Don't copy the member if it points to one of our nodes,
     259                // just keep a note to re-add it later on.
     260                if (allNodes.contains(rm.member)) {
     261                    rolesToReAdd.add(rm.role);
     262                } else {
     263                    newRel.members.add(rm);
     264                }
     265            }
     266            for (String role : rolesToReAdd) {
     267                newRel.members.add(new RelationMember(role, dest));
     268            }
     269            cmds.add(new ChangeCommand(r, newRel));
     270        }
     271
     272        Main.main.undoRedo.add(new SequenceCommand(tr("Merge {0} nodes", allNodes.size()), cmds));
     273        Main.ds.setSelected(dest);
     274
     275        return dest;
     276    }
     277
     278
     279    /**
     280    * Enable the "Merge Nodes" menu option if more then one node is selected
     281    */
     282    public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) {
     283        boolean ok = true;
     284        if (newSelection.size() < 2) {
     285            setEnabled(false);
     286            return;
     287        }
     288        for (OsmPrimitive osm : newSelection) {
     289            if (!(osm instanceof Node)) {
     290                ok = false;
     291                break;
     292            }
     293        }
     294        setEnabled(ok);
     295    }
    296296}
  • trunk/src/org/openstreetmap/josm/actions/MoveAction.java

    r1084 r1169  
    2626public class MoveAction extends JosmAction {
    2727
    28         public enum Direction { UP, LEFT, RIGHT, DOWN }
    29         private Direction myDirection;
     28    public enum Direction { UP, LEFT, RIGHT, DOWN }
     29    private Direction myDirection;
    3030
    31         // any better idea?
    32         private static Object calltosupermustbefirststatementinconstructor(Direction dir, boolean text) {
    33                 Shortcut sc;
    34                 String directiontext;
    35                 if        (dir == Direction.UP)   {
    36                         directiontext = tr("up");
    37                         sc = Shortcut.registerShortcut("core:moveup",    tr("Move objects {0}", directiontext), KeyEvent.VK_UP,    Shortcut.GROUPS_ALT1+Shortcut.GROUP_DIRECT);
    38                 } else if (dir == Direction.DOWN)  {
    39                         directiontext = tr("down");
    40                         sc = Shortcut.registerShortcut("core:movedown",  tr("Move objects {0}", directiontext), KeyEvent.VK_DOWN,  Shortcut.GROUPS_ALT1+Shortcut.GROUP_DIRECT);
    41                 } else if (dir == Direction.LEFT)  {
    42                         directiontext = tr("left");
    43                         sc = Shortcut.registerShortcut("core:moveleft",  tr("Move objects {0}", directiontext), KeyEvent.VK_LEFT,  Shortcut.GROUPS_ALT1+Shortcut.GROUP_DIRECT);
    44                 } else { //dir == Direction.RIGHT) {
    45                         directiontext = tr("right");
    46                         sc = Shortcut.registerShortcut("core:moveright", tr("Move objects {0}", directiontext), KeyEvent.VK_RIGHT, Shortcut.GROUPS_ALT1+Shortcut.GROUP_DIRECT);
    47                 }
    48                 if (text) {
    49                         return directiontext;
    50                 } else {
    51                         return sc;
    52                 }
    53         }
     31    // any better idea?
     32    private static Object calltosupermustbefirststatementinconstructor(Direction dir, boolean text) {
     33        Shortcut sc;
     34        String directiontext;
     35        if        (dir == Direction.UP)   {
     36            directiontext = tr("up");
     37            sc = Shortcut.registerShortcut("core:moveup",    tr("Move objects {0}", directiontext), KeyEvent.VK_UP,    Shortcut.GROUPS_ALT1+Shortcut.GROUP_DIRECT);
     38        } else if (dir == Direction.DOWN)  {
     39            directiontext = tr("down");
     40            sc = Shortcut.registerShortcut("core:movedown",  tr("Move objects {0}", directiontext), KeyEvent.VK_DOWN,  Shortcut.GROUPS_ALT1+Shortcut.GROUP_DIRECT);
     41        } else if (dir == Direction.LEFT)  {
     42            directiontext = tr("left");
     43            sc = Shortcut.registerShortcut("core:moveleft",  tr("Move objects {0}", directiontext), KeyEvent.VK_LEFT,  Shortcut.GROUPS_ALT1+Shortcut.GROUP_DIRECT);
     44        } else { //dir == Direction.RIGHT) {
     45            directiontext = tr("right");
     46            sc = Shortcut.registerShortcut("core:moveright", tr("Move objects {0}", directiontext), KeyEvent.VK_RIGHT, Shortcut.GROUPS_ALT1+Shortcut.GROUP_DIRECT);
     47        }
     48        if (text) {
     49            return directiontext;
     50        } else {
     51            return sc;
     52        }
     53    }
    5454
    55         public MoveAction(Direction dir) {
    56                 super(tr("Move {0}", calltosupermustbefirststatementinconstructor(dir, true)), null,
    57                       tr("Moves Objects {0}", calltosupermustbefirststatementinconstructor(dir, true)),
    58                       (Shortcut)calltosupermustbefirststatementinconstructor(dir, false), true);
    59                 myDirection = dir;
    60         }
     55    public MoveAction(Direction dir) {
     56        super(tr("Move {0}", calltosupermustbefirststatementinconstructor(dir, true)), null,
     57              tr("Moves Objects {0}", calltosupermustbefirststatementinconstructor(dir, true)),
     58              (Shortcut)calltosupermustbefirststatementinconstructor(dir, false), true);
     59        myDirection = dir;
     60    }
    6161
    62         public void actionPerformed(ActionEvent event) {
     62    public void actionPerformed(ActionEvent event) {
    6363
    64                 // find out how many "real" units the objects have to be moved in order to
    65                 // achive an 1-pixel movement
     64        // find out how many "real" units the objects have to be moved in order to
     65        // achive an 1-pixel movement
    6666
    67                 EastNorth en1 = Main.map.mapView.getEastNorth(100, 100);
    68                 EastNorth en2 = Main.map.mapView.getEastNorth(101, 101);
     67        EastNorth en1 = Main.map.mapView.getEastNorth(100, 100);
     68        EastNorth en2 = Main.map.mapView.getEastNorth(101, 101);
    6969
    70                 double distx = en2.east() - en1.east();
    71                 double disty = en2.north() - en1.north();
     70        double distx = en2.east() - en1.east();
     71        double disty = en2.north() - en1.north();
    7272
    73                 switch (myDirection) {
    74                 case UP:
    75                         distx = 0;
    76                         disty = -disty;
    77                         break;
    78                 case DOWN:
    79                         distx = 0;
    80                         break;
    81                 case LEFT:
    82                         disty = 0;
    83                         distx = -distx;
    84                 default:
    85                         disty = 0;
    86                 }
     73        switch (myDirection) {
     74        case UP:
     75            distx = 0;
     76            disty = -disty;
     77            break;
     78        case DOWN:
     79            distx = 0;
     80            break;
     81        case LEFT:
     82            disty = 0;
     83            distx = -distx;
     84        default:
     85            disty = 0;
     86        }
    8787
    88                 Collection<OsmPrimitive> selection = Main.ds.getSelected();
    89                 Collection<Node> affectedNodes = AllNodesVisitor.getAllNodes(selection);
     88        Collection<OsmPrimitive> selection = Main.ds.getSelected();
     89        Collection<Node> affectedNodes = AllNodesVisitor.getAllNodes(selection);
    9090
    91                 Command c = !Main.main.undoRedo.commands.isEmpty()
    92                 ? Main.main.undoRedo.commands.getLast() : null;
     91        Command c = !Main.main.undoRedo.commands.isEmpty()
     92        ? Main.main.undoRedo.commands.getLast() : null;
    9393
    94                 if (c instanceof MoveCommand && affectedNodes.equals(((MoveCommand)c).objects))
    95                         ((MoveCommand)c).moveAgain(distx, disty);
    96                 else
    97                         Main.main.undoRedo.add(
    98                                         c = new MoveCommand(selection, distx, disty));
     94        if (c instanceof MoveCommand && affectedNodes.equals(((MoveCommand)c).objects))
     95            ((MoveCommand)c).moveAgain(distx, disty);
     96        else
     97            Main.main.undoRedo.add(
     98                    c = new MoveCommand(selection, distx, disty));
    9999
    100                 for (Node n : affectedNodes) {
    101                         if (n.coor.isOutSideWorld()) {
    102                                 // Revert move
    103                                 ((MoveCommand) c).moveAgain(-distx, -disty);
    104                                 JOptionPane.showMessageDialog(Main.parent,
    105                                                 tr("Cannot move objects outside of the world."));
    106                                 return;
    107                         }
    108                 }
     100        for (Node n : affectedNodes) {
     101            if (n.coor.isOutSideWorld()) {
     102                // Revert move
     103                ((MoveCommand) c).moveAgain(-distx, -disty);
     104                JOptionPane.showMessageDialog(Main.parent,
     105                        tr("Cannot move objects outside of the world."));
     106                return;
     107            }
     108        }
    109109
    110                 Main.map.mapView.repaint();
    111         }
     110        Main.map.mapView.repaint();
     111    }
    112112}
  • trunk/src/org/openstreetmap/josm/actions/NewAction.java

    r1084 r1169  
    1414public class NewAction extends JosmAction {
    1515
    16         public NewAction() {
    17                 super(tr("New"), "new", tr("Create a new map."),
    18                 Shortcut.registerShortcut("system:new", tr("File: {0}", tr("New")), KeyEvent.VK_N, Shortcut.GROUP_MENU), true);
    19         }
     16    public NewAction() {
     17        super(tr("New"), "new", tr("Create a new map."),
     18        Shortcut.registerShortcut("system:new", tr("File: {0}", tr("New")), KeyEvent.VK_N, Shortcut.GROUP_MENU), true);
     19    }
    2020
    21         public void actionPerformed(ActionEvent e) {
    22                 Main.main.addLayer(new OsmDataLayer(new DataSet(), tr("unnamed"), null));
    23         }
     21    public void actionPerformed(ActionEvent e) {
     22        Main.main.addLayer(new OsmDataLayer(new DataSet(), tr("unnamed"), null));
     23    }
    2424}
  • trunk/src/org/openstreetmap/josm/actions/OpenFileAction.java

    r1167 r1169  
    3535public class OpenFileAction extends DiskAccessAction {
    3636
    37         /**
    38          * Create an open action. The name is "Open a file".
    39          */
    40         public OpenFileAction() {
    41                 super(tr("Open ..."), "open", tr("Open a file."),
    42                 Shortcut.registerShortcut("system:open", tr("File: {0}", tr("Open ...")), KeyEvent.VK_O, Shortcut.GROUP_MENU));
    43         }
    44 
    45         public void actionPerformed(ActionEvent e) {
    46                 JFileChooser fc = createAndOpenFileChooser(true, true, null);
    47                 if (fc == null)
    48                         return;
    49                 File[] files = fc.getSelectedFiles();
    50                 for (int i = files.length; i > 0; --i)
    51                         openFile(files[i-1]);
    52         }
    53 
    54         /**
    55          * Open the given file.
    56          */
    57         public void openFile(File file) {
    58                 try {
    59                         if (asGpxData(file.getName()))
    60                                 openFileAsGpx(file);
    61                         else if (asNmeaData(file.getName()))
    62                                 openFileAsNmea(file);
    63                         else
    64                                 openAsData(file);
    65                 } catch (SAXException x) {
    66                         x.printStackTrace();
    67                         JOptionPane.showMessageDialog(Main.parent, tr("Error while parsing {0}",file.getName())+": "+x.getMessage());
    68                 } catch (IOException x) {
    69                         x.printStackTrace();
    70                         JOptionPane.showMessageDialog(Main.parent, tr("Could not read \"{0}\"",file.getName())+"\n"+x.getMessage());
    71                 }
    72         }
    73 
    74         private void openAsData(File file) throws SAXException, IOException, FileNotFoundException {
    75                 String fn = file.getName();
    76                 if (ExtensionFileFilter.filters[ExtensionFileFilter.OSM].acceptName(fn)) {
    77                         DataSet dataSet = OsmReader.parseDataSet(new FileInputStream(file), null, Main.pleaseWaitDlg);
    78                         OsmDataLayer layer = new OsmDataLayer(dataSet, file.getName(), file);
    79                         Main.main.addLayer(layer);
    80                         layer.fireDataChange();
    81                 }
    82                 else
    83                         JOptionPane.showMessageDialog(Main.parent, fn+": "+tr("Unknown file extension: {0}", fn.substring(file.getName().lastIndexOf('.')+1)));
    84         }
    85 
    86         private void openFileAsGpx(File file) throws SAXException, IOException, FileNotFoundException {
    87                 String fn = file.getName();
    88                 if (ExtensionFileFilter.filters[ExtensionFileFilter.GPX].acceptName(fn)) {
    89                         GpxReader r = null;
    90                         InputStream is;
    91                         if (file.getName().endsWith(".gpx.gz")) {
    92                                 is = new GZIPInputStream(new FileInputStream(file));
    93                         } else {
    94                                 is = new FileInputStream(file);
    95                         }
    96                         // Workaround for SAX BOM bug
    97                         // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6206835
    98                         if(!((is.read()==0xef)&&(is.read()==0xbb)&&(is.read()==0xbf))) {
    99                                 is.close();
    100                                 if (file.getName().endsWith(".gpx.gz")) {
    101                                         is = new GZIPInputStream(new FileInputStream(file));
    102                                 } else {
    103                                         is = new FileInputStream(file);
    104                                 }
    105                         }
    106                         r = new GpxReader(is,file.getAbsoluteFile().getParentFile());
    107                         r.data.storageFile = file;
    108                         GpxLayer gpxLayer = new GpxLayer(r.data, fn);
    109                         Main.main.addLayer(gpxLayer);
    110                         if (Main.pref.getBoolean("marker.makeautomarkers", true)) {
    111                                 MarkerLayer ml = new MarkerLayer(r.data, tr("Markers from {0}", fn), file, gpxLayer);
    112                                 if (ml.data.size() > 0) {
    113                                         Main.main.addLayer(ml);
    114                                 }
    115                         }
    116 
    117                 } else {
    118                         throw new IllegalStateException();
    119                 }
    120     }
    121        
    122         private void showNmeaInfobox(boolean success, NmeaReader r) {
    123                 String msg = tr("Coordinates imported: ") + r.getNumberOfCoordinates() + "\n" +
    124                 tr("Malformed sentences: ") + r.getParserMalformed() + "\n" +
    125                 tr("Checksum errors: ") + r.getParserChecksumErrors() + "\n";
    126                 if(!success) // don't scare the user unneccessarily
    127                         msg += tr("Unknown sentences: ") + r.getParserUnknown() + "\n";
    128                 msg += tr("Zero coordinates: ") + r.getParserZeroCoordinates();
    129                 if(success) {   
    130                         JOptionPane.showMessageDialog(
    131                                 Main.parent, msg,
    132                                 tr("NMEA import success"),JOptionPane.INFORMATION_MESSAGE);
    133                 } else {
    134                         JOptionPane.showMessageDialog(
    135                                 Main.parent, msg,
    136                                 tr("NMEA import faliure!"),JOptionPane.ERROR_MESSAGE);
    137                 }
    138         }
    139 
    140         private void openFileAsNmea(File file) throws IOException, FileNotFoundException {
    141                 String fn = file.getName();
    142                 if (ExtensionFileFilter.filters[ExtensionFileFilter.NMEA].acceptName(fn)) {
    143                         NmeaReader r = new NmeaReader(new FileInputStream(file), file.getAbsoluteFile().getParentFile());
    144                         if(r.getNumberOfCoordinates()>0) {
    145                                 r.data.storageFile = file;
    146                                 GpxLayer gpxLayer = new GpxLayer(r.data, fn);
    147                                 Main.main.addLayer(gpxLayer);
    148                                 if (Main.pref.getBoolean("marker.makeautomarkers", true)) {
    149                                         MarkerLayer ml = new MarkerLayer(r.data, tr("Markers from {0}", fn), file, gpxLayer);
    150                                         if (ml.data.size() > 0) {
    151                                                 Main.main.addLayer(ml);
    152                                         }
    153                                 }
    154                         }
    155                         showNmeaInfobox(r.getNumberOfCoordinates()>0, r);
    156                 } else {
    157                         throw new IllegalStateException();
    158                 }
     37    /**
     38     * Create an open action. The name is "Open a file".
     39     */
     40    public OpenFileAction() {
     41        super(tr("Open ..."), "open", tr("Open a file."),
     42        Shortcut.registerShortcut("system:open", tr("File: {0}", tr("Open ...")), KeyEvent.VK_O, Shortcut.GROUP_MENU));
    15943    }
    16044
    161         private boolean asGpxData(String fn) {
    162                 return ExtensionFileFilter.filters[ExtensionFileFilter.GPX].acceptName(fn);
    163         }
     45    public void actionPerformed(ActionEvent e) {
     46        JFileChooser fc = createAndOpenFileChooser(true, true, null);
     47        if (fc == null)
     48            return;
     49        File[] files = fc.getSelectedFiles();
     50        for (int i = files.length; i > 0; --i)
     51            openFile(files[i-1]);
     52    }
    16453
    165         private boolean asNmeaData(String fn) {
    166                 return ExtensionFileFilter.filters[ExtensionFileFilter.NMEA].acceptName(fn);
    167         }
     54    /**
     55     * Open the given file.
     56     */
     57    public void openFile(File file) {
     58        try {
     59            if (asGpxData(file.getName()))
     60                openFileAsGpx(file);
     61            else if (asNmeaData(file.getName()))
     62                openFileAsNmea(file);
     63            else
     64                openAsData(file);
     65        } catch (SAXException x) {
     66            x.printStackTrace();
     67            JOptionPane.showMessageDialog(Main.parent, tr("Error while parsing {0}",file.getName())+": "+x.getMessage());
     68        } catch (IOException x) {
     69            x.printStackTrace();
     70            JOptionPane.showMessageDialog(Main.parent, tr("Could not read \"{0}\"",file.getName())+"\n"+x.getMessage());
     71        }
     72    }
     73
     74    private void openAsData(File file) throws SAXException, IOException, FileNotFoundException {
     75        String fn = file.getName();
     76        if (ExtensionFileFilter.filters[ExtensionFileFilter.OSM].acceptName(fn)) {
     77            DataSet dataSet = OsmReader.parseDataSet(new FileInputStream(file), null, Main.pleaseWaitDlg);
     78            OsmDataLayer layer = new OsmDataLayer(dataSet, file.getName(), file);
     79            Main.main.addLayer(layer);
     80            layer.fireDataChange();
     81        }
     82        else
     83            JOptionPane.showMessageDialog(Main.parent, fn+": "+tr("Unknown file extension: {0}", fn.substring(file.getName().lastIndexOf('.')+1)));
     84    }
     85
     86    private void openFileAsGpx(File file) throws SAXException, IOException, FileNotFoundException {
     87        String fn = file.getName();
     88        if (ExtensionFileFilter.filters[ExtensionFileFilter.GPX].acceptName(fn)) {
     89            GpxReader r = null;
     90            InputStream is;
     91            if (file.getName().endsWith(".gpx.gz")) {
     92                is = new GZIPInputStream(new FileInputStream(file));
     93            } else {
     94                is = new FileInputStream(file);
     95            }
     96            // Workaround for SAX BOM bug
     97            // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6206835
     98            if(!((is.read()==0xef)&&(is.read()==0xbb)&&(is.read()==0xbf))) {
     99                is.close();
     100                if (file.getName().endsWith(".gpx.gz")) {
     101                    is = new GZIPInputStream(new FileInputStream(file));
     102                } else {
     103                    is = new FileInputStream(file);
     104                }
     105            }
     106            r = new GpxReader(is,file.getAbsoluteFile().getParentFile());
     107            r.data.storageFile = file;
     108            GpxLayer gpxLayer = new GpxLayer(r.data, fn);
     109            Main.main.addLayer(gpxLayer);
     110            if (Main.pref.getBoolean("marker.makeautomarkers", true)) {
     111                MarkerLayer ml = new MarkerLayer(r.data, tr("Markers from {0}", fn), file, gpxLayer);
     112                if (ml.data.size() > 0) {
     113                    Main.main.addLayer(ml);
     114                }
     115            }
     116
     117        } else {
     118            throw new IllegalStateException();
     119        }
     120    }
     121
     122    private void showNmeaInfobox(boolean success, NmeaReader r) {
     123        String msg = tr("Coordinates imported: ") + r.getNumberOfCoordinates() + "\n" +
     124        tr("Malformed sentences: ") + r.getParserMalformed() + "\n" +
     125        tr("Checksum errors: ") + r.getParserChecksumErrors() + "\n";
     126        if(!success) // don't scare the user unneccessarily
     127            msg += tr("Unknown sentences: ") + r.getParserUnknown() + "\n";
     128        msg += tr("Zero coordinates: ") + r.getParserZeroCoordinates();
     129        if(success) {
     130            JOptionPane.showMessageDialog(
     131                Main.parent, msg,
     132                tr("NMEA import success"),JOptionPane.INFORMATION_MESSAGE);
     133        } else {
     134            JOptionPane.showMessageDialog(
     135                Main.parent, msg,
     136                tr("NMEA import faliure!"),JOptionPane.ERROR_MESSAGE);
     137        }
     138    }
     139
     140    private void openFileAsNmea(File file) throws IOException, FileNotFoundException {
     141        String fn = file.getName();
     142        if (ExtensionFileFilter.filters[ExtensionFileFilter.NMEA].acceptName(fn)) {
     143            NmeaReader r = new NmeaReader(new FileInputStream(file), file.getAbsoluteFile().getParentFile());
     144            if(r.getNumberOfCoordinates()>0) {
     145                r.data.storageFile = file;
     146                GpxLayer gpxLayer = new GpxLayer(r.data, fn);
     147                Main.main.addLayer(gpxLayer);
     148                if (Main.pref.getBoolean("marker.makeautomarkers", true)) {
     149                    MarkerLayer ml = new MarkerLayer(r.data, tr("Markers from {0}", fn), file, gpxLayer);
     150                    if (ml.data.size() > 0) {
     151                        Main.main.addLayer(ml);
     152                    }
     153                }
     154            }
     155            showNmeaInfobox(r.getNumberOfCoordinates()>0, r);
     156        } else {
     157            throw new IllegalStateException();
     158        }
     159    }
     160
     161    private boolean asGpxData(String fn) {
     162        return ExtensionFileFilter.filters[ExtensionFileFilter.GPX].acceptName(fn);
     163    }
     164
     165    private boolean asNmeaData(String fn) {
     166        return ExtensionFileFilter.filters[ExtensionFileFilter.NMEA].acceptName(fn);
     167    }
    168168
    169169
  • trunk/src/org/openstreetmap/josm/actions/OpenLocationAction.java

    r1148 r1169  
    4242public class OpenLocationAction extends JosmAction {
    4343
    44         /**
    45         * Create an open action. The name is "Open a file".
    46         */
    47         public OpenLocationAction() {
    48                 super(tr("Open Location..."), "openlocation", tr("Open a URL."),
    49                 Shortcut.registerShortcut("system:open_location", tr("File: {0}", tr("Open Location...")), KeyEvent.VK_L, Shortcut.GROUP_MENU), true);
    50         }
     44    /**
     45    * Create an open action. The name is "Open a file".
     46    */
     47    public OpenLocationAction() {
     48        super(tr("Open Location..."), "openlocation", tr("Open a URL."),
     49        Shortcut.registerShortcut("system:open_location", tr("File: {0}", tr("Open Location...")), KeyEvent.VK_L, Shortcut.GROUP_MENU), true);
     50    }
    5151
    52         public void actionPerformed(ActionEvent e) {
     52    public void actionPerformed(ActionEvent e) {
    5353
    54             JCheckBox layer = new JCheckBox(tr("Separate Layer"));
    55             layer.setSelected(Main.pref.getBoolean("download.newlayer"));
    56             JPanel all = new JPanel(new GridBagLayout());
    57             all.add(new JLabel("Enter URL to download:"), GBC.eol());
    58             JTextField urltext = new JTextField(40);
    59             all.add(urltext, GBC.eol());
    60             all.add(layer, GBC.eol());
    61             int answer = JOptionPane.showConfirmDialog(Main.parent, all, tr("Download Location"), JOptionPane.OK_CANCEL_OPTION);
    62             if (answer != JOptionPane.OK_OPTION)
    63                 return;
    64             openUrl(layer.isSelected(), urltext.getText());
    65         }
     54        JCheckBox layer = new JCheckBox(tr("Separate Layer"));
     55        layer.setSelected(Main.pref.getBoolean("download.newlayer"));
     56        JPanel all = new JPanel(new GridBagLayout());
     57        all.add(new JLabel("Enter URL to download:"), GBC.eol());
     58        JTextField urltext = new JTextField(40);
     59        all.add(urltext, GBC.eol());
     60        all.add(layer, GBC.eol());
     61        int answer = JOptionPane.showConfirmDialog(Main.parent, all, tr("Download Location"), JOptionPane.OK_CANCEL_OPTION);
     62        if (answer != JOptionPane.OK_OPTION)
     63            return;
     64        openUrl(layer.isSelected(), urltext.getText());
     65    }
    6666
    67         /**
    68         * Open the given file.
    69         */
    70         public void openUrl(boolean new_layer, String url) {
    71             new DownloadOsmTask().loadUrl(new_layer, url);
    72         }
     67    /**
     68    * Open the given file.
     69    */
     70    public void openUrl(boolean new_layer, String url) {
     71        new DownloadOsmTask().loadUrl(new_layer, url);
     72    }
    7373
    7474}
  • trunk/src/org/openstreetmap/josm/actions/OrthogonalizeAction.java

    r1090 r1169  
    2828
    2929/**
    30  * Align edges of a way so all angles are right angles. 
    31  * 
     30 * Align edges of a way so all angles are right angles.
     31 *
    3232 * 1. Find orientation of all edges
    3333 * 2. Compute main orientation, weighted by length of edge, normalized to angles between 0 and pi/2
     
    3838public final class OrthogonalizeAction extends JosmAction {
    3939
    40         public OrthogonalizeAction() {
    41         super(tr("Orthogonalize shape"), 
    42             "ortho", 
     40    public OrthogonalizeAction() {
     41        super(tr("Orthogonalize shape"),
     42            "ortho",
    4343            tr("Move nodes so all angles are 90 or 270deg"),
    44             Shortcut.registerShortcut("tools:orthogonalize", tr("Tool: {0}", tr("Orthogonalize")), 
    45             KeyEvent.VK_Q, 
     44            Shortcut.registerShortcut("tools:orthogonalize", tr("Tool: {0}", tr("Orthogonalize")),
     45            KeyEvent.VK_Q,
    4646            Shortcut.GROUP_EDIT), true);
    47         }
     47    }
    4848
    4949    public void actionPerformed(ActionEvent e) {
     
    6060                    JOptionPane.showMessageDialog(Main.parent, tr("Only two nodes allowed"));
    6161                    return;
    62                 } 
     62                }
    6363                dirnodes.add((Node) osm);
    6464                continue;
     
    6868                JOptionPane.showMessageDialog(Main.parent, tr("Selection must consist only of ways."));
    6969                return;
    70             } 
     70            }
    7171
    7272            // Check if every way is made of at least four segments and closed
     
    7878
    7979            // Check if every edge in the way is a definite edge of at least 45 degrees of direction change
    80             // Otherwise, two segments could be turned into same direction and intersection would fail. 
     80            // Otherwise, two segments could be turned into same direction and intersection would fail.
    8181            // Or changes of shape would be too serious.
    8282            for (int i1=0; i1 < way.nodes.size()-1; i1++) {
     
    116116        boolean use_dirnodes = false;
    117117
    118         if (dirnodes.size() == 2) { 
    119             // When selection contains two nodes, use the nodes to compute a direction 
     118        if (dirnodes.size() == 2) {
     119            // When selection contains two nodes, use the nodes to compute a direction
    120120            // to align all ways to
    121121            align_to_heading = normalize_angle(dirnodes.get(0).eastNorth.heading(dirnodes.get(1).eastNorth));
     
    124124
    125125        for (OsmPrimitive osm : sel) {
    126             if(!(osm instanceof Way)) 
     126            if(!(osm instanceof Way))
    127127                continue;
    128128
     
    164164
    165165                if (angle_diff_max > Math.PI/3) {
    166                     // rearrange headings: everything < 0 gets PI/2-rotated 
     166                    // rearrange headings: everything < 0 gets PI/2-rotated
    167167                    for (int i=0; i < sides; i++) {
    168                         if (headings[i] < 0) 
     168                        if (headings[i] < 0)
    169169                            headings[i] += Math.PI/2;
    170170                    }
     
    183183                    sum_weights += weights[i];
    184184                }
    185                 align_to_heading = normalize_angle(sum_weighted_headings/sum_weights); 
     185                align_to_heading = normalize_angle(sum_weighted_headings/sum_weights);
    186186            }
    187187
    188188
    189189            for (int i=0; i < sides; i++) {
    190                 // Compute handy indices of three nodes to be used in one loop iteration. 
    191                 // We use segments (i1,i2) and (i2,i3), align them and compute the new 
     190                // Compute handy indices of three nodes to be used in one loop iteration.
     191                // We use segments (i1,i2) and (i2,i3), align them and compute the new
    192192                // position of the i2-node as the intersection of the realigned (i1,i2), (i2,i3) segments
    193193                // Not the most efficient algorithm, but we don't handle millions of nodes...
     
    212212
    213213                // compute intersection of segments
    214                 double u=det(B.east() - A.east(), B.north() - A.north(), 
     214                double u=det(B.east() - A.east(), B.north() - A.north(),
    215215                        C.east() - D.east(), C.north() - D.north());
    216216
    217217                // Check for parallel segments and do nothing if they are
    218                 // In practice this will probably only happen when a way has 
     218                // In practice this will probably only happen when a way has
    219219                // been duplicated
    220220
     
    225225                // if the segment is scaled to length 1
    226226
    227                 double q = det(B.north() - C.north(), B.east() - C.east(), 
     227                double q = det(B.north() - C.north(), B.east() - C.east(),
    228228                        D.north() - C.north(), D.east() - C.east()) / u;
    229229                EastNorth intersection = new EastNorth(
     
    239239                    cmds.add(new MoveCommand(n, dx, dy));
    240240                }
    241             } 
     241            }
    242242        }
    243243
  • trunk/src/org/openstreetmap/josm/actions/PasteAction.java

    r1084 r1169  
    2929
    3030    public PasteAction() {
    31         super(tr("Paste"), "paste", tr("Paste contents of paste buffer."),
    32                         Shortcut.registerShortcut("system:paste", tr("Edit: {0}", tr("Paste")), KeyEvent.VK_V, Shortcut.GROUP_MENU), true);
    33                         setEnabled(false);
     31        super(tr("Paste"), "paste", tr("Paste contents of paste buffer."),
     32            Shortcut.registerShortcut("system:paste", tr("Edit: {0}", tr("Paste")), KeyEvent.VK_V, Shortcut.GROUP_MENU), true);
     33            setEnabled(false);
    3434    }
    3535
    36         public void actionPerformed(ActionEvent e) {
    37                 DataSet pasteBuffer = Main.pasteBuffer;
     36    public void actionPerformed(ActionEvent e) {
     37        DataSet pasteBuffer = Main.pasteBuffer;
    3838
    39                 /* Find the middle of the pasteBuffer area */
    40                 double maxEast = -1E100, minEast = 1E100, maxNorth = -1E100, minNorth = 1E100;
    41                 for (Node n : pasteBuffer.nodes) {
    42                         double east = n.eastNorth.east();
    43                         double north = n.eastNorth.north();
    44                         if (east > maxEast) { maxEast = east; }
    45                         if (east < minEast) { minEast = east; }
    46                         if (north > maxNorth) { maxNorth = north; }
    47                         if (north < minNorth) { minNorth = north; }
    48                 }
     39        /* Find the middle of the pasteBuffer area */
     40        double maxEast = -1E100, minEast = 1E100, maxNorth = -1E100, minNorth = 1E100;
     41        for (Node n : pasteBuffer.nodes) {
     42            double east = n.eastNorth.east();
     43            double north = n.eastNorth.north();
     44            if (east > maxEast) { maxEast = east; }
     45            if (east < minEast) { minEast = east; }
     46            if (north > maxNorth) { maxNorth = north; }
     47            if (north < minNorth) { minNorth = north; }
     48        }
    4949
    50                 EastNorth mPosition;
    51                 if((e.getModifiers() & ActionEvent.CTRL_MASK) ==0){
    52                         mPosition = Main.map.mapView.getCenter();
    53                 } else {
    54                         mPosition = Main.map.mapView.getEastNorth(Main.map.mapView.lastMEvent.getX(), Main.map.mapView.lastMEvent.getY());
    55                 }
     50        EastNorth mPosition;
     51        if((e.getModifiers() & ActionEvent.CTRL_MASK) ==0){
     52            mPosition = Main.map.mapView.getCenter();
     53        } else {
     54            mPosition = Main.map.mapView.getEastNorth(Main.map.mapView.lastMEvent.getX(), Main.map.mapView.lastMEvent.getY());
     55        }
    5656
    57                 double offsetEast  = mPosition.east() - (maxEast + minEast)/2.0;
    58                 double offsetNorth = mPosition.north() - (maxNorth + minNorth)/2.0;
     57        double offsetEast  = mPosition.east() - (maxEast + minEast)/2.0;
     58        double offsetNorth = mPosition.north() - (maxNorth + minNorth)/2.0;
    5959
    60                 HashMap<OsmPrimitive,OsmPrimitive> map = new HashMap<OsmPrimitive,OsmPrimitive>();
    61                   /* temporarily maps old nodes to new so we can do a true deep copy */
     60        HashMap<OsmPrimitive,OsmPrimitive> map = new HashMap<OsmPrimitive,OsmPrimitive>();
     61          /* temporarily maps old nodes to new so we can do a true deep copy */
    6262
    63                 /* do the deep copy of the paste buffer contents, leaving the pasteBuffer unchanged */
    64                 for (Node n : pasteBuffer.nodes) {
    65                         Node nnew = new Node(n);
    66                         nnew.id = 0;
    67                         /* adjust the coordinates to the middle of the visible map area */
    68                         nnew.eastNorth = new EastNorth(nnew.eastNorth.east() + offsetEast, nnew.eastNorth.north() + offsetNorth);
    69                         nnew.coor = Main.proj.eastNorth2latlon(nnew.eastNorth);
    70                         map.put(n, nnew);
    71                 }
    72                 for (Way w : pasteBuffer.ways) {
    73                         Way wnew = new Way();
    74                         wnew.cloneFrom(w);
    75                         wnew.id = 0;
    76                         /* make sure we reference the new nodes corresponding to the old ones */
    77                         List<Node> nodes = new ArrayList<Node>();
    78                         for (Node n : w.nodes) {
    79                                 nodes.add((Node)map.get(n));
    80                         }
    81                         wnew.nodes.clear();
    82                         wnew.nodes.addAll(nodes);
    83                         map.put(w, wnew);
    84                 }
    85                 for (Relation r : pasteBuffer.relations) {
    86                         Relation rnew = new Relation(r);
    87                         rnew.id = 0;
    88                         List<RelationMember> members = new ArrayList<RelationMember>();
    89                         for (RelationMember m : r.members) {
    90                                 RelationMember mnew = new RelationMember(m);
    91                                 mnew.member = map.get(m.member);
    92                                 members.add(mnew);
    93                         }
    94                         rnew.members.clear();
    95                         rnew.members.addAll(members);
    96                         map.put(r, rnew);
    97                 }
     63        /* do the deep copy of the paste buffer contents, leaving the pasteBuffer unchanged */
     64        for (Node n : pasteBuffer.nodes) {
     65            Node nnew = new Node(n);
     66            nnew.id = 0;
     67            /* adjust the coordinates to the middle of the visible map area */
     68            nnew.eastNorth = new EastNorth(nnew.eastNorth.east() + offsetEast, nnew.eastNorth.north() + offsetNorth);
     69            nnew.coor = Main.proj.eastNorth2latlon(nnew.eastNorth);
     70            map.put(n, nnew);
     71        }
     72        for (Way w : pasteBuffer.ways) {
     73            Way wnew = new Way();
     74            wnew.cloneFrom(w);
     75            wnew.id = 0;
     76            /* make sure we reference the new nodes corresponding to the old ones */
     77            List<Node> nodes = new ArrayList<Node>();
     78            for (Node n : w.nodes) {
     79                nodes.add((Node)map.get(n));
     80            }
     81            wnew.nodes.clear();
     82            wnew.nodes.addAll(nodes);
     83            map.put(w, wnew);
     84        }
     85        for (Relation r : pasteBuffer.relations) {
     86            Relation rnew = new Relation(r);
     87            rnew.id = 0;
     88            List<RelationMember> members = new ArrayList<RelationMember>();
     89            for (RelationMember m : r.members) {
     90                RelationMember mnew = new RelationMember(m);
     91                mnew.member = map.get(m.member);
     92                members.add(mnew);
     93            }
     94            rnew.members.clear();
     95            rnew.members.addAll(members);
     96            map.put(r, rnew);
     97        }
    9898
    99                 /* Now execute the commands to add the dupicated contents of the paste buffer to the map */
    100                 Collection<OsmPrimitive> osms = map.values();
    101                 Collection<Command> clist = new LinkedList<Command>();
    102                 for (OsmPrimitive osm : osms) {
    103                         clist.add(new AddCommand(osm));
    104                 }
     99        /* Now execute the commands to add the dupicated contents of the paste buffer to the map */
     100        Collection<OsmPrimitive> osms = map.values();
     101        Collection<Command> clist = new LinkedList<Command>();
     102        for (OsmPrimitive osm : osms) {
     103            clist.add(new AddCommand(osm));
     104        }
    105105
    106                 Main.main.undoRedo.add(new SequenceCommand(tr("Paste"), clist));
    107                 Main.ds.setSelected(osms);
    108                 Main.map.mapView.repaint();
     106        Main.main.undoRedo.add(new SequenceCommand(tr("Paste"), clist));
     107        Main.ds.setSelected(osms);
     108        Main.map.mapView.repaint();
    109109    }
    110110}
  • trunk/src/org/openstreetmap/josm/actions/PasteTagsAction.java

    r1084 r1169  
    2424public final class PasteTagsAction extends JosmAction implements SelectionChangedListener {
    2525
    26         public PasteTagsAction(JosmAction copyAction) {
    27                 super(tr("Paste Tags"), "pastetags",
    28                         tr("Apply tags of contents of paste buffer to all selected items."),
    29                         Shortcut.registerShortcut("system:pastestyle", tr("Edit: {0}", tr("Paste Tags")), KeyEvent.VK_V, Shortcut.GROUP_MENU, Shortcut.SHIFT_DEFAULT), true);
    30                 DataSet.selListeners.add(this);
    31                 copyAction.addListener(this);
    32                 setEnabled(false);
    33         }
     26    public PasteTagsAction(JosmAction copyAction) {
     27        super(tr("Paste Tags"), "pastetags",
     28            tr("Apply tags of contents of paste buffer to all selected items."),
     29            Shortcut.registerShortcut("system:pastestyle", tr("Edit: {0}", tr("Paste Tags")), KeyEvent.VK_V, Shortcut.GROUP_MENU, Shortcut.SHIFT_DEFAULT), true);
     30        DataSet.selListeners.add(this);
     31        copyAction.addListener(this);
     32        setEnabled(false);
     33    }
    3434
    35         private void pasteKeys(Collection<Command> clist, Collection<? extends OsmPrimitive> pasteBufferSubset, Collection<OsmPrimitive> selectionSubset) {
    36                 /* scan the paste buffer, and add tags to each of the selected objects.
    37                 * If a tag already exists, it is overwritten */
    38                 if (selectionSubset != null && ! selectionSubset.isEmpty()) {
    39                         for (Iterator<? extends OsmPrimitive> it = pasteBufferSubset.iterator(); it.hasNext();) {
    40                                 OsmPrimitive osm = it.next();
    41                                 Map<String, String> m = osm.keys;
    42                                 if(m != null)
    43                                 {
    44                                         for (String key : m.keySet()) {
    45                                                 if (! key.equals("created_by"))
    46                                                         clist.add(new ChangePropertyCommand(selectionSubset, key, osm.keys.get(key)));
    47                                         }
    48                                 }
    49                         }
    50                 }
    51         }
     35    private void pasteKeys(Collection<Command> clist, Collection<? extends OsmPrimitive> pasteBufferSubset, Collection<OsmPrimitive> selectionSubset) {
     36        /* scan the paste buffer, and add tags to each of the selected objects.
     37        * If a tag already exists, it is overwritten */
     38        if (selectionSubset != null && ! selectionSubset.isEmpty()) {
     39            for (Iterator<? extends OsmPrimitive> it = pasteBufferSubset.iterator(); it.hasNext();) {
     40                OsmPrimitive osm = it.next();
     41                Map<String, String> m = osm.keys;
     42                if(m != null)
     43                {
     44                    for (String key : m.keySet()) {
     45                        if (! key.equals("created_by"))
     46                            clist.add(new ChangePropertyCommand(selectionSubset, key, osm.keys.get(key)));
     47                    }
     48                }
     49            }
     50        }
     51    }
    5252
    53         public void actionPerformed(ActionEvent e) {
    54                 Collection<Command> clist = new LinkedList<Command>();
    55                 pasteKeys(clist, Main.pasteBuffer.nodes, Main.ds.getSelectedNodes());
    56                 pasteKeys(clist, Main.pasteBuffer.ways, Main.ds.getSelectedWays());
    57                 pasteKeys(clist, Main.pasteBuffer.relations, Main.ds.getSelectedRelations());
    58                 Main.main.undoRedo.add(new SequenceCommand(tr("Paste Tags"), clist));
    59                 Main.ds.setSelected(Main.ds.getSelected()); // to force selection listeners, in particular the tag panel, to update
    60                 Main.map.mapView.repaint();
    61         }
     53    public void actionPerformed(ActionEvent e) {
     54        Collection<Command> clist = new LinkedList<Command>();
     55        pasteKeys(clist, Main.pasteBuffer.nodes, Main.ds.getSelectedNodes());
     56        pasteKeys(clist, Main.pasteBuffer.ways, Main.ds.getSelectedWays());
     57        pasteKeys(clist, Main.pasteBuffer.relations, Main.ds.getSelectedRelations());
     58        Main.main.undoRedo.add(new SequenceCommand(tr("Paste Tags"), clist));
     59        Main.ds.setSelected(Main.ds.getSelected()); // to force selection listeners, in particular the tag panel, to update
     60        Main.map.mapView.repaint();
     61    }
    6262
    63         private boolean containsSameKeysWithDifferentValues(Collection<? extends OsmPrimitive> osms) {
    64                 Map<String,String> kvSeen = new HashMap<String,String>();
    65                 for (Iterator<? extends OsmPrimitive> it = osms.iterator(); it.hasNext();) {
    66                         OsmPrimitive osm = it.next();
    67                         if (osm.keys == null || osm.keys.isEmpty())
    68                                 continue;
    69                         for (String key : osm.keys.keySet()) {
    70                                 if (key.equals("created_by")) // we ignore created_by
    71                                         continue;
    72                                 String value = osm.keys.get(key);
    73                                 if (! kvSeen.containsKey(key))
    74                                         kvSeen.put(key, value);
    75                                 else if (! kvSeen.get(key).equals(value))
    76                                         return true;
    77                         }
    78                 }
    79                 return false;
    80         }
     63    private boolean containsSameKeysWithDifferentValues(Collection<? extends OsmPrimitive> osms) {
     64        Map<String,String> kvSeen = new HashMap<String,String>();
     65        for (Iterator<? extends OsmPrimitive> it = osms.iterator(); it.hasNext();) {
     66            OsmPrimitive osm = it.next();
     67            if (osm.keys == null || osm.keys.isEmpty())
     68                continue;
     69            for (String key : osm.keys.keySet()) {
     70                if (key.equals("created_by")) // we ignore created_by
     71                    continue;
     72                String value = osm.keys.get(key);
     73                if (! kvSeen.containsKey(key))
     74                    kvSeen.put(key, value);
     75                else if (! kvSeen.get(key).equals(value))
     76                    return true;
     77            }
     78        }
     79        return false;
     80    }
    8181
    82         /**
    83         * Determines whether to enable the widget depending on the contents of the paste
    84         * buffer and current selection
    85         * @param pasteBuffer
    86         */
    87         private void possiblyEnable(Collection<? extends OsmPrimitive> selection, DataSet pasteBuffer) {
    88                 /* only enable if there is something selected to paste into and
    89                         if we don't have conflicting keys in the pastebuffer */
    90                 setEnabled(selection != null &&
    91                                 ! selection.isEmpty() &&
    92                                 ! pasteBuffer.allPrimitives().isEmpty() &&
    93                                 (Main.ds.getSelectedNodes().isEmpty() ||
    94                                         ! containsSameKeysWithDifferentValues(pasteBuffer.nodes)) &&
    95                                 (Main.ds.getSelectedWays().isEmpty() ||
    96                                         ! containsSameKeysWithDifferentValues(pasteBuffer.ways)) &&
    97                                 (Main.ds.getSelectedRelations().isEmpty() ||
    98                                         ! containsSameKeysWithDifferentValues(pasteBuffer.relations)));
    99         }
     82    /**
     83    * Determines whether to enable the widget depending on the contents of the paste
     84    * buffer and current selection
     85    * @param pasteBuffer
     86    */
     87    private void possiblyEnable(Collection<? extends OsmPrimitive> selection, DataSet pasteBuffer) {
     88        /* only enable if there is something selected to paste into and
     89            if we don't have conflicting keys in the pastebuffer */
     90        setEnabled(selection != null &&
     91                ! selection.isEmpty() &&
     92                ! pasteBuffer.allPrimitives().isEmpty() &&
     93                (Main.ds.getSelectedNodes().isEmpty() ||
     94                    ! containsSameKeysWithDifferentValues(pasteBuffer.nodes)) &&
     95                (Main.ds.getSelectedWays().isEmpty() ||
     96                    ! containsSameKeysWithDifferentValues(pasteBuffer.ways)) &&
     97                (Main.ds.getSelectedRelations().isEmpty() ||
     98                    ! containsSameKeysWithDifferentValues(pasteBuffer.relations)));
     99    }
    100100
    101         @Override public void pasteBufferChanged(DataSet newPasteBuffer) {
    102                 possiblyEnable(Main.ds.getSelected(), newPasteBuffer);
    103         }
     101    @Override public void pasteBufferChanged(DataSet newPasteBuffer) {
     102        possiblyEnable(Main.ds.getSelected(), newPasteBuffer);
     103    }
    104104
    105         public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) {
    106                 possiblyEnable(newSelection, Main.pasteBuffer);
    107         }
     105    public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) {
     106        possiblyEnable(newSelection, Main.pasteBuffer);
     107    }
    108108}
  • trunk/src/org/openstreetmap/josm/actions/PreferencesAction.java

    r1084 r1169  
    2525public class PreferencesAction extends JosmAction {
    2626
    27         /**
    28         * Create the preference action with "&Preferences" as label.
    29         */
    30         public PreferencesAction() {
    31                 super(tr("Preferences ..."), "preference", tr("Open a preferences page for global settings."),
    32                 Shortcut.registerShortcut("system:preferences", tr("Preferences"), KeyEvent.VK_F12, Shortcut.GROUP_DIRECT), true);
    33         }
     27    /**
     28    * Create the preference action with "&Preferences" as label.
     29    */
     30    public PreferencesAction() {
     31        super(tr("Preferences ..."), "preference", tr("Open a preferences page for global settings."),
     32        Shortcut.registerShortcut("system:preferences", tr("Preferences"), KeyEvent.VK_F12, Shortcut.GROUP_DIRECT), true);
     33    }
    3434
    35         /**
    36         * Launch the preferences dialog.
    37         */
    38         public void actionPerformed(ActionEvent e) {
    39                 PreferenceDialog prefDlg = new PreferenceDialog();
    40                 prefDlg.setMinimumSize(new Dimension(400,300));
    41                 JPanel prefPanel = new JPanel(new GridBagLayout());
    42                 prefPanel.add(prefDlg, GBC.eol().fill(GBC.BOTH));
     35    /**
     36    * Launch the preferences dialog.
     37    */
     38    public void actionPerformed(ActionEvent e) {
     39        PreferenceDialog prefDlg = new PreferenceDialog();
     40        prefDlg.setMinimumSize(new Dimension(400,300));
     41        JPanel prefPanel = new JPanel(new GridBagLayout());
     42        prefPanel.add(prefDlg, GBC.eol().fill(GBC.BOTH));
    4343
    44                 JOptionPane pane = new JOptionPane(prefPanel, JOptionPane.PLAIN_MESSAGE, JOptionPane.OK_CANCEL_OPTION);
    45                 JDialog dlg = pane.createDialog(Main.parent, tr("Preferences"));
    46                 dlg.setResizable(true);
    47                 dlg.setMinimumSize(new Dimension(500,400));
     44        JOptionPane pane = new JOptionPane(prefPanel, JOptionPane.PLAIN_MESSAGE, JOptionPane.OK_CANCEL_OPTION);
     45        JDialog dlg = pane.createDialog(Main.parent, tr("Preferences"));
     46        dlg.setResizable(true);
     47        dlg.setMinimumSize(new Dimension(500,400));
    4848
    49 //              if (dlg.getWidth() > 600)
    50 //                      dlg.setSize(600, dlg.getHeight());
    51 //              if (dlg.getHeight() > 600)
    52 //                      dlg.setSize(dlg.getWidth(),600);
     49//      if (dlg.getWidth() > 600)
     50//          dlg.setSize(600, dlg.getHeight());
     51//      if (dlg.getHeight() > 600)
     52//          dlg.setSize(dlg.getWidth(),600);
    5353
    54                 int JOSMWidth = Main.parent.getWidth();
    55                 int JOSMHeight = Main.parent.getHeight();
     54        int JOSMWidth = Main.parent.getWidth();
     55        int JOSMHeight = Main.parent.getHeight();
    5656
    57                 if (JOSMWidth > 2000 && JOSMWidth >  JOSMHeight * 2)
    58                         // don't center on horizontal span monitor configurations (yes, can be selfish sometimes)
    59                         JOSMWidth /= 2;
     57        if (JOSMWidth > 2000 && JOSMWidth >  JOSMHeight * 2)
     58            // don't center on horizontal span monitor configurations (yes, can be selfish sometimes)
     59            JOSMWidth /= 2;
    6060
    61                 int targetWidth = JOSMWidth / 2;
    62                 if (targetWidth < 600) targetWidth = 600;
    63                 if (targetWidth > 1200) targetWidth = 1200;
    64                 int targetHeight = (JOSMHeight * 3) / 4;
    65                 if (targetHeight < 600) targetHeight = 600;
    66                 if (targetHeight > 1200) targetHeight = 1200;
     61        int targetWidth = JOSMWidth / 2;
     62        if (targetWidth < 600) targetWidth = 600;
     63        if (targetWidth > 1200) targetWidth = 1200;
     64        int targetHeight = (JOSMHeight * 3) / 4;
     65        if (targetHeight < 600) targetHeight = 600;
     66        if (targetHeight > 1200) targetHeight = 1200;
    6767
    68                 int targetX = Main.parent.getX() + JOSMWidth / 2 - targetWidth / 2;
    69                 int targetY = Main.parent.getY() + JOSMHeight / 2 - targetHeight / 2;
     68        int targetX = Main.parent.getX() + JOSMWidth / 2 - targetWidth / 2;
     69        int targetY = Main.parent.getY() + JOSMHeight / 2 - targetHeight / 2;
    7070
    71                 dlg.setBounds(targetX, targetY, targetWidth, targetHeight);
     71        dlg.setBounds(targetX, targetY, targetWidth, targetHeight);
    7272
    73                 dlg.setVisible(true);
    74                 if (pane.getValue() instanceof Integer && (Integer)pane.getValue() == JOptionPane.OK_OPTION)
    75                         prefDlg.ok();
    76         }
     73        dlg.setVisible(true);
     74        if (pane.getValue() instanceof Integer && (Integer)pane.getValue() == JOptionPane.OK_OPTION)
     75            prefDlg.ok();
     76    }
    7777}
  • trunk/src/org/openstreetmap/josm/actions/RedoAction.java

    r1084 r1169  
    1717public class RedoAction extends JosmAction {
    1818
    19         /**
    20         * Construct the action with "Redo" as label.
    21         */
    22         public RedoAction() {
    23                 super(tr("Redo"), "redo", tr("Redo the last undone action."),
    24                 Shortcut.registerShortcut("system:redo", tr("Edit: {0}", tr("Redo")), KeyEvent.VK_Y, Shortcut.GROUP_MENU), true);
    25                 setEnabled(false);
    26         }
     19    /**
     20    * Construct the action with "Redo" as label.
     21    */
     22    public RedoAction() {
     23        super(tr("Redo"), "redo", tr("Redo the last undone action."),
     24        Shortcut.registerShortcut("system:redo", tr("Edit: {0}", tr("Redo")), KeyEvent.VK_Y, Shortcut.GROUP_MENU), true);
     25        setEnabled(false);
     26    }
    2727
    28         public void actionPerformed(ActionEvent e) {
    29                 if (Main.map == null)
    30                         return;
    31                 Main.map.repaint();
    32                 Main.main.undoRedo.redo();
    33         }
     28    public void actionPerformed(ActionEvent e) {
     29        if (Main.map == null)
     30            return;
     31        Main.map.repaint();
     32        Main.main.undoRedo.redo();
     33    }
    3434}
  • trunk/src/org/openstreetmap/josm/actions/RenameLayerAction.java

    r655 r1169  
    2121 * Action to rename an specific layer. Provides the option to rename the
    2222 * file, this layer was loaded from as well (if it was loaded from a file).
    23  * 
     23 *
    2424 * @author Imi
    2525 */
    2626public class RenameLayerAction extends AbstractAction {
    2727
    28         private File file;
    29         private Layer layer;
     28    private File file;
     29    private Layer layer;
    3030
    31         /**
    32         * @param file The file of the original location of this layer.
    33          *              If null, no possibility to "rename the file as well" is provided.
    34         */
    35         public RenameLayerAction(File file, Layer layer) {
    36                 super(tr("Rename layer"), ImageProvider.get("dialogs", "edit"));
    37                 this.file = file;
    38                 this.layer = layer;
    39                 this.putValue("help", "Action/LayerRename");
    40         }
     31    /**
     32    * @param file The file of the original location of this layer.
     33     *      If null, no possibility to "rename the file as well" is provided.
     34    */
     35    public RenameLayerAction(File file, Layer layer) {
     36        super(tr("Rename layer"), ImageProvider.get("dialogs", "edit"));
     37        this.file = file;
     38        this.layer = layer;
     39        this.putValue("help", "Action/LayerRename");
     40    }
    4141
    42         public void actionPerformed(ActionEvent e) {
    43                 Box panel = Box.createVerticalBox();
    44                 final JTextField name = new JTextField(layer.name);
    45                 panel.add(name);
    46                 JCheckBox filerename = new JCheckBox(tr("Also rename the file"));
    47                 if (Main.applet) {
    48                         filerename.setEnabled(false);
    49                         filerename.setSelected(false);
    50                 } else {
    51                         panel.add(filerename);
    52                         filerename.setEnabled(file != null);
    53                 }
    54                 if (filerename.isEnabled())
    55                         filerename.setSelected(Main.pref.getBoolean("layer.rename-file", true));
     42    public void actionPerformed(ActionEvent e) {
     43        Box panel = Box.createVerticalBox();
     44        final JTextField name = new JTextField(layer.name);
     45        panel.add(name);
     46        JCheckBox filerename = new JCheckBox(tr("Also rename the file"));
     47        if (Main.applet) {
     48            filerename.setEnabled(false);
     49            filerename.setSelected(false);
     50        } else {
     51            panel.add(filerename);
     52            filerename.setEnabled(file != null);
     53        }
     54        if (filerename.isEnabled())
     55            filerename.setSelected(Main.pref.getBoolean("layer.rename-file", true));
    5656
    57                 final JOptionPane optionPane = new JOptionPane(panel, JOptionPane.QUESTION_MESSAGE, JOptionPane.OK_CANCEL_OPTION){
    58                         @Override public void selectInitialValue() {
    59                                 name.requestFocusInWindow();
    60                                 name.selectAll();
    61                         }
    62                 };
    63                 final JDialog dlg = optionPane.createDialog(Main.parent, tr("Rename layer"));
    64                 dlg.setVisible(true);
     57        final JOptionPane optionPane = new JOptionPane(panel, JOptionPane.QUESTION_MESSAGE, JOptionPane.OK_CANCEL_OPTION){
     58            @Override public void selectInitialValue() {
     59                name.requestFocusInWindow();
     60                name.selectAll();
     61            }
     62        };
     63        final JDialog dlg = optionPane.createDialog(Main.parent, tr("Rename layer"));
     64        dlg.setVisible(true);
    6565
    66                 Object answer = optionPane.getValue();
    67                 if (answer == null || answer == JOptionPane.UNINITIALIZED_VALUE ||
    68                                 (answer instanceof Integer && (Integer)answer != JOptionPane.OK_OPTION)) {
    69                         return;
    70                 }
     66        Object answer = optionPane.getValue();
     67        if (answer == null || answer == JOptionPane.UNINITIALIZED_VALUE ||
     68                (answer instanceof Integer && (Integer)answer != JOptionPane.OK_OPTION)) {
     69            return;
     70        }
    7171
    72                 String nameText = name.getText();
    73                 if (filerename.isEnabled()) {
    74                         Main.pref.put("layer.rename-file", filerename.isSelected());
    75                         if (filerename.isSelected()) {
    76                                 String newname = nameText;
    77                                 if (newname.indexOf("/") == -1 && newname.indexOf("\\") == -1)
    78                                         newname = file.getParent() + File.separator + newname;
    79                                 String oldname = file.getName();
    80                                 if (name.getText().indexOf('.') == -1 && oldname.indexOf('.') >= 0)
    81                                         newname += oldname.substring(oldname.lastIndexOf('.'));
    82                                 File newFile = new File(newname);
    83                                 if (file.renameTo(newFile)) {
    84                                         layer.associatedFile = newFile;
    85                                         nameText = newFile.getName();
    86                                 } else {
    87                                         JOptionPane.showMessageDialog(Main.parent, tr("Could not rename the file \"{0}\".", file.getPath()));
    88                                         return;
    89                                 }
    90                         }
    91                 }
    92                 layer.name = nameText;
    93                 Main.parent.repaint();
    94         }
     72        String nameText = name.getText();
     73        if (filerename.isEnabled()) {
     74            Main.pref.put("layer.rename-file", filerename.isSelected());
     75            if (filerename.isSelected()) {
     76                String newname = nameText;
     77                if (newname.indexOf("/") == -1 && newname.indexOf("\\") == -1)
     78                    newname = file.getParent() + File.separator + newname;
     79                String oldname = file.getName();
     80                if (name.getText().indexOf('.') == -1 && oldname.indexOf('.') >= 0)
     81                    newname += oldname.substring(oldname.lastIndexOf('.'));
     82                File newFile = new File(newname);
     83                if (file.renameTo(newFile)) {
     84                    layer.associatedFile = newFile;
     85                    nameText = newFile.getName();
     86                } else {
     87                    JOptionPane.showMessageDialog(Main.parent, tr("Could not rename the file \"{0}\".", file.getPath()));
     88                    return;
     89                }
     90            }
     91        }
     92        layer.name = nameText;
     93        Main.parent.repaint();
     94    }
    9595}
  • trunk/src/org/openstreetmap/josm/actions/ReverseWayAction.java

    r1084 r1169  
    2828public final class ReverseWayAction extends JosmAction {
    2929
    30         public ReverseWayAction() {
    31                 super(tr("Reverse ways"), "wayflip", tr("Reverse the direction of all selected ways."),
    32                 Shortcut.registerShortcut("tools:reverse", tr("Tool: {0}", tr("Reverse ways")), KeyEvent.VK_R, Shortcut.GROUP_EDIT), true);
    33         }
     30    public ReverseWayAction() {
     31        super(tr("Reverse ways"), "wayflip", tr("Reverse the direction of all selected ways."),
     32        Shortcut.registerShortcut("tools:reverse", tr("Tool: {0}", tr("Reverse ways")), KeyEvent.VK_R, Shortcut.GROUP_EDIT), true);
     33    }
    3434
    35         public void actionPerformed(ActionEvent e) {
    36                 final Collection<Way> sel = new LinkedList<Way>();
    37                 new Visitor() {
    38                         public void visit(Node n) {
    39                         }
     35    public void actionPerformed(ActionEvent e) {
     36        final Collection<Way> sel = new LinkedList<Way>();
     37        new Visitor() {
     38            public void visit(Node n) {
     39            }
    4040
    41                         public void visit(Way w) {
    42                                 sel.add(w);
    43                         }
     41            public void visit(Way w) {
     42                sel.add(w);
     43            }
    4444
    45                         public void visit(Relation e) {
    46                         }
     45            public void visit(Relation e) {
     46            }
    4747
    48                         public void visitAll() {
    49                                 for (OsmPrimitive osm : Main.ds.getSelected())
    50                                         osm.visit(this);
    51                         }
    52                 }.visitAll();
     48            public void visitAll() {
     49                for (OsmPrimitive osm : Main.ds.getSelected())
     50                    osm.visit(this);
     51            }
     52        }.visitAll();
    5353
    54                 if (sel.isEmpty()) {
    55                         JOptionPane.showMessageDialog(Main.parent,
    56                                 tr("Please select at least one way."));
    57                         return;
    58                 }
     54        if (sel.isEmpty()) {
     55            JOptionPane.showMessageDialog(Main.parent,
     56                    tr("Please select at least one way."));
     57            return;
     58        }
    5959
    60                 boolean propertiesUpdated = false;
    61                 ReverseWayTagCorrector reverseWayTagCorrector = new ReverseWayTagCorrector();
    62                 Collection<Command> c = new LinkedList<Command>();
    63                 for (Way w : sel) {
    64                         Way wnew = new Way(w);
    65                         Collections.reverse(wnew.nodes);
    66                         if (Main.pref.getBoolean("tag-correction.reverse-way", true)) {
    67                                 try
    68                                 {
    69                                         final Collection<Command> changePropertyCommands = reverseWayTagCorrector.execute(wnew);
    70                                         propertiesUpdated = propertiesUpdated
    71                                         || (changePropertyCommands != null && !changePropertyCommands.isEmpty());
    72                                         c.addAll(changePropertyCommands);
    73                                 }
    74                                 catch(UserCancelException ex)
    75                                 {
    76                                         return;
    77                                 }
    78                         }
    79                         c.add(new ChangeCommand(w, wnew));
    80                 }
    81                 Main.main.undoRedo.add(new SequenceCommand(tr("Reverse ways"), c));
    82                 if (propertiesUpdated)
    83                         DataSet.fireSelectionChanged(Main.ds.getSelected());
    84                 Main.map.repaint();
    85         }
     60        boolean propertiesUpdated = false;
     61        ReverseWayTagCorrector reverseWayTagCorrector = new ReverseWayTagCorrector();
     62        Collection<Command> c = new LinkedList<Command>();
     63        for (Way w : sel) {
     64            Way wnew = new Way(w);
     65            Collections.reverse(wnew.nodes);
     66            if (Main.pref.getBoolean("tag-correction.reverse-way", true)) {
     67                try
     68                {
     69                    final Collection<Command> changePropertyCommands = reverseWayTagCorrector.execute(wnew);
     70                    propertiesUpdated = propertiesUpdated
     71                        || (changePropertyCommands != null && !changePropertyCommands.isEmpty());
     72                    c.addAll(changePropertyCommands);
     73                }
     74                catch(UserCancelException ex)
     75                {
     76                    return;
     77                }
     78            }
     79            c.add(new ChangeCommand(w, wnew));
     80        }
     81        Main.main.undoRedo.add(new SequenceCommand(tr("Reverse ways"), c));
     82        if (propertiesUpdated)
     83            DataSet.fireSelectionChanged(Main.ds.getSelected());
     84        Main.map.repaint();
     85    }
    8686}
  • trunk/src/org/openstreetmap/josm/actions/SaveAction.java

    r1084 r1169  
    1919public class SaveAction extends SaveActionBase {
    2020
    21         /**
    22         * Construct the action with "Save" as label.
    23         * @param layer Save this layer.
    24         */
    25         public SaveAction(Layer layer) {
    26                 super(tr("Save"), "save", tr("Save the current data."),
    27                 Shortcut.registerShortcut("system:save", tr("File: {0}", tr("Save")), KeyEvent.VK_S, Shortcut.GROUP_MENU), layer);
    28         }
     21    /**
     22    * Construct the action with "Save" as label.
     23    * @param layer Save this layer.
     24    */
     25    public SaveAction(Layer layer) {
     26        super(tr("Save"), "save", tr("Save the current data."),
     27        Shortcut.registerShortcut("system:save", tr("File: {0}", tr("Save")), KeyEvent.VK_S, Shortcut.GROUP_MENU), layer);
     28    }
    2929
    30         @Override public File getFile(Layer layer) {
    31                 if (layer instanceof OsmDataLayer) {
    32                         File f = ((OsmDataLayer)layer).associatedFile;
    33                         if (f != null) {
    34                                 return f;
    35                         }
    36                 }
    37                 if (layer instanceof GpxLayer) {
    38                         File f = ((GpxLayer)layer).data.storageFile;
    39                         if (f != null) {
    40                                 return f;
    41                         }
    42                 }
    43                 return openFileDialog(layer);
    44         }
     30    @Override public File getFile(Layer layer) {
     31        if (layer instanceof OsmDataLayer) {
     32            File f = ((OsmDataLayer)layer).associatedFile;
     33            if (f != null) {
     34                return f;
     35            }
     36        }
     37        if (layer instanceof GpxLayer) {
     38            File f = ((GpxLayer)layer).data.storageFile;
     39            if (f != null) {
     40                return f;
     41            }
     42        }
     43        return openFileDialog(layer);
     44    }
    4545}
  • trunk/src/org/openstreetmap/josm/actions/SaveActionBase.java

    r1084 r1169  
    2626public abstract class SaveActionBase extends DiskAccessAction {
    2727
    28         private Layer layer;
    29 
    30         public SaveActionBase(String name, String iconName, String tooltip, Shortcut shortcut, Layer layer) {
    31                 super(name, iconName, tooltip, shortcut);
    32                 this.layer = layer;
    33         }
    34 
    35         @Deprecated
    36         public SaveActionBase(String name, String iconName, String tooltip, int shortcut, int modifiers, Layer layer) {
    37                 super(name, iconName, tooltip, shortcut, modifiers);
    38                 this.layer = layer;
    39         }
    40 
    41         public void actionPerformed(ActionEvent e) {
    42                 Layer layer = this.layer;
    43                 if (layer == null && Main.map != null && (Main.map.mapView.getActiveLayer() instanceof OsmDataLayer
    44                                 || Main.map.mapView.getActiveLayer() instanceof GpxLayer))
    45                         layer = Main.map.mapView.getActiveLayer();
    46                 if (layer == null)
    47                         layer = Main.main.editLayer();
    48 
    49                 if (!checkSaveConditions(layer))
    50                         return;
    51 
    52 
    53                 File file = getFile(layer);
    54                 if (file == null)
    55                         return;
    56 
    57                 save(file, layer);
    58 
    59                 layer.name = file.getName();
    60                 layer.associatedFile = file;
    61                 Main.parent.repaint();
    62         }
    63 
    64         protected abstract File getFile(Layer layer);
    65 
    66         /**
    67         * Checks whether it is ok to launch a save (whether we have data,
    68         * there is no conflict etc.)
    69         * @return <code>true</code>, if it is safe to save.
    70         */
    71         public boolean checkSaveConditions(Layer layer) {
    72                 if (layer == null) {
    73                         JOptionPane.showMessageDialog(Main.parent, tr("Internal Error: cannot check conditions for no layer. Please report this as a bug."));
    74                         return false;
    75                 }
    76                 if (Main.map == null) {
    77                         JOptionPane.showMessageDialog(Main.parent, tr("No document open so nothing to save."));
    78                         return false;
    79                 }
    80 
    81                 if (layer instanceof OsmDataLayer && isDataSetEmpty((OsmDataLayer)layer) && JOptionPane.NO_OPTION == JOptionPane.showConfirmDialog(Main.parent,tr("The document contains no data. Save anyway?"), tr("Empty document"), JOptionPane.YES_NO_OPTION)) {
    82                         return false;
    83                 }
    84                 if (layer instanceof GpxLayer && ((GpxLayer)layer).data == null) {
    85                         return false;
    86                 }
    87                 if (!Main.map.conflictDialog.conflicts.isEmpty()) {
    88                         int answer = JOptionPane.showConfirmDialog(Main.parent,
    89                                         tr("There are unresolved conflicts. Conflicts will not be saved and handled as if you rejected all. Continue?"),tr("Conflicts"), JOptionPane.YES_NO_OPTION);
    90                         if (answer != JOptionPane.YES_OPTION)
    91                                 return false;
    92                 }
    93                 return true;
    94         }
    95 
    96         public static File openFileDialog(Layer layer) {
    97                 JFileChooser fc = createAndOpenFileChooser(false, false, layer instanceof GpxLayer ? tr("Save GPX file") : tr("Save OSM file"));
    98                 if (fc == null)
    99                         return null;
    100 
    101                 File file = fc.getSelectedFile();
    102 
    103                 String fn = file.getPath();
    104                 if (fn.indexOf('.') == -1) {
    105                         FileFilter ff = fc.getFileFilter();
    106                         if (ff instanceof ExtensionFileFilter)
    107                                 fn += "." + ((ExtensionFileFilter)ff).defaultExtension;
    108                         else if (layer instanceof GpxLayer)
    109                                 fn += ".gpx";
    110                         else
    111                                 fn += ".osm";
    112                         file = new File(fn);
    113                 }
    114                 return file;
    115         }
    116 
    117         private static void copy(File src, File dst) throws IOException {
    118                 FileInputStream srcStream;
    119                 FileOutputStream dstStream;
    120                 try {
    121                         srcStream = new FileInputStream(src);
    122                         dstStream = new FileOutputStream(dst);
    123                 } catch (FileNotFoundException e) {
    124                         JOptionPane.showMessageDialog(Main.parent, tr("Could not back up file.")+"\n"+e.getMessage());
    125                         return;
    126                 }
    127                 byte buf[] = new byte[1<<16];
    128                 int len;
    129                 while ((len = srcStream.read(buf)) != -1) {
    130                         dstStream.write(buf, 0, len);
    131                 }
    132                 srcStream.close();
    133                 dstStream.close();
    134         }
    135 
    136         public static void save(File file, Layer layer) {
    137                 if (layer instanceof GpxLayer) {
    138                         save(file, (GpxLayer)layer);
    139                         ((GpxLayer)layer).data.storageFile = file;
    140                 } else if (layer instanceof OsmDataLayer) {
    141                         save(file, (OsmDataLayer)layer);
    142                 }
    143         }
    144 
    145         public static void save(File file, OsmDataLayer layer) {
    146                 File tmpFile = null;
    147                 try {
    148                         if (ExtensionFileFilter.filters[ExtensionFileFilter.GPX].acceptName(file.getPath())) {
    149                                 GpxExportAction.exportGpx(file, layer);
    150                         } else if (ExtensionFileFilter.filters[ExtensionFileFilter.OSM].acceptName(file.getPath())) {
    151                                 // use a tmp file because if something errors out in the
    152                                 // process of writing the file, we might just end up with
    153                                 // a truncated file.  That can destroy lots of work.
    154                                 if (file.exists()) {
    155                                         tmpFile = new File(file.getPath() + "~");
    156                                         copy(file, tmpFile);
    157                                 }
    158                                 OsmWriter.output(new FileOutputStream(file), new OsmWriter.All(layer.data, false));
    159                                 if (!Main.pref.getBoolean("save.keepbackup") && (tmpFile != null))
    160                                         tmpFile.delete();
    161                         } else {
    162                                 JOptionPane.showMessageDialog(Main.parent, tr("Unknown file extension."));
    163                                 return;
    164                         }
    165                         layer.cleanData(null, false);
    166                 } catch (IOException e) {
    167                         e.printStackTrace();
    168                         JOptionPane.showMessageDialog(Main.parent, tr("An error occurred while saving.")+"\n"+e.getMessage());
    169 
    170                         try {
    171                                 // if the file save failed, then the tempfile will not
    172                                 // be deleted.  So, restore the backup if we made one.
    173                                 if (tmpFile != null && tmpFile.exists()) {
    174                                         copy(tmpFile, file);
    175                                 }
    176                         } catch (IOException e2) {
    177                                 e2.printStackTrace();
    178                                 JOptionPane.showMessageDialog(Main.parent, tr("An error occurred while restoring backup file.")+"\n"+e2.getMessage());
    179                         }
    180                 }
    181         }
    182 
    183         public static void save(File file, GpxLayer layer) {
    184                 File tmpFile = null;
    185                 try {
    186                         if (ExtensionFileFilter.filters[ExtensionFileFilter.GPX].acceptName(file.getPath())) {
    187 
    188                                 // use a tmp file because if something errors out in the
    189                                 // process of writing the file, we might just end up with
    190                                 // a truncated file.  That can destroy lots of work.
    191                                 if (file.exists()) {
    192                                         tmpFile = new File(file.getPath() + "~");
    193                                         copy(file, tmpFile);
    194                                 }
    195                                 FileOutputStream fo = new FileOutputStream(file);
    196                                 new GpxWriter(fo).write(layer.data);
    197                                 fo.flush();
    198                                 fo.close();
    199 
    200                                 if (!Main.pref.getBoolean("save.keepbackup") && (tmpFile != null)) {
    201                                         tmpFile.delete();
    202                                 }
    203                         } else {
    204                                 JOptionPane.showMessageDialog(Main.parent, tr("Unknown file extension."));
    205                                 return;
    206                         }
    207                 } catch (IOException e) {
    208                         e.printStackTrace();
    209                         JOptionPane.showMessageDialog(Main.parent, tr("An error occurred while saving.")+"\n"+e.getMessage());
    210                 }
    211                 try {
    212                         // if the file save failed, then the tempfile will not
    213                         // be deleted.  So, restore the backup if we made one.
    214                         if (tmpFile != null && tmpFile.exists()) {
    215                                 copy(tmpFile, file);
    216                         }
    217                 } catch (IOException e) {
    218                         e.printStackTrace();
    219                         JOptionPane.showMessageDialog(Main.parent, tr("An error occurred while restoring backup file.")+"\n"+e.getMessage());
    220                 }
    221         }
    222 
    223         /**
    224         * Check the data set if it would be empty on save. It is empty, if it contains
    225         * no objects (after all objects that are created and deleted without being
    226         * transfered to the server have been removed).
    227         *
    228         * @return <code>true</code>, if a save result in an empty data set.
    229         */
    230         private boolean isDataSetEmpty(OsmDataLayer layer) {
    231                 for (OsmPrimitive osm : layer.data.allNonDeletedPrimitives())
    232                         if (!osm.deleted || osm.id > 0)
    233                                 return false;
    234                 return true;
    235         }
     28    private Layer layer;
     29
     30    public SaveActionBase(String name, String iconName, String tooltip, Shortcut shortcut, Layer layer) {
     31        super(name, iconName, tooltip, shortcut);
     32        this.layer = layer;
     33    }
     34
     35    @Deprecated
     36    public SaveActionBase(String name, String iconName, String tooltip, int shortcut, int modifiers, Layer layer) {
     37        super(name, iconName, tooltip, shortcut, modifiers);
     38        this.layer = layer;
     39    }
     40
     41    public void actionPerformed(ActionEvent e) {
     42        Layer layer = this.layer;
     43        if (layer == null && Main.map != null && (Main.map.mapView.getActiveLayer() instanceof OsmDataLayer
     44                || Main.map.mapView.getActiveLayer() instanceof GpxLayer))
     45            layer = Main.map.mapView.getActiveLayer();
     46        if (layer == null)
     47            layer = Main.main.editLayer();
     48
     49        if (!checkSaveConditions(layer))
     50            return;
     51
     52
     53        File file = getFile(layer);
     54        if (file == null)
     55            return;
     56
     57        save(file, layer);
     58
     59        layer.name = file.getName();
     60        layer.associatedFile = file;
     61        Main.parent.repaint();
     62    }
     63
     64    protected abstract File getFile(Layer layer);
     65
     66    /**
     67    * Checks whether it is ok to launch a save (whether we have data,
     68    * there is no conflict etc.)
     69    * @return <code>true</code>, if it is safe to save.
     70    */
     71    public boolean checkSaveConditions(Layer layer) {
     72        if (layer == null) {
     73            JOptionPane.showMessageDialog(Main.parent, tr("Internal Error: cannot check conditions for no layer. Please report this as a bug."));
     74            return false;
     75        }
     76        if (Main.map == null) {
     77            JOptionPane.showMessageDialog(Main.parent, tr("No document open so nothing to save."));
     78            return false;
     79        }
     80
     81        if (layer instanceof OsmDataLayer && isDataSetEmpty((OsmDataLayer)layer) && JOptionPane.NO_OPTION == JOptionPane.showConfirmDialog(Main.parent,tr("The document contains no data. Save anyway?"), tr("Empty document"), JOptionPane.YES_NO_OPTION)) {
     82            return false;
     83        }
     84        if (layer instanceof GpxLayer && ((GpxLayer)layer).data == null) {
     85            return false;
     86        }
     87        if (!Main.map.conflictDialog.conflicts.isEmpty()) {
     88            int answer = JOptionPane.showConfirmDialog(Main.parent,
     89                    tr("There are unresolved conflicts. Conflicts will not be saved and handled as if you rejected all. Continue?"),tr("Conflicts"), JOptionPane.YES_NO_OPTION);
     90            if (answer != JOptionPane.YES_OPTION)
     91                return false;
     92        }
     93        return true;
     94    }
     95
     96    public static File openFileDialog(Layer layer) {
     97        JFileChooser fc = createAndOpenFileChooser(false, false, layer instanceof GpxLayer ? tr("Save GPX file") : tr("Save OSM file"));
     98        if (fc == null)
     99            return null;
     100
     101        File file = fc.getSelectedFile();
     102
     103        String fn = file.getPath();
     104        if (fn.indexOf('.') == -1) {
     105            FileFilter ff = fc.getFileFilter();
     106            if (ff instanceof ExtensionFileFilter)
     107                fn += "." + ((ExtensionFileFilter)ff).defaultExtension;
     108            else if (layer instanceof GpxLayer)
     109                fn += ".gpx";
     110            else
     111                fn += ".osm";
     112            file = new File(fn);
     113        }
     114        return file;
     115    }
     116
     117    private static void copy(File src, File dst) throws IOException {
     118        FileInputStream srcStream;
     119        FileOutputStream dstStream;
     120        try {
     121            srcStream = new FileInputStream(src);
     122            dstStream = new FileOutputStream(dst);
     123        } catch (FileNotFoundException e) {
     124            JOptionPane.showMessageDialog(Main.parent, tr("Could not back up file.")+"\n"+e.getMessage());
     125            return;
     126        }
     127        byte buf[] = new byte[1<<16];
     128        int len;
     129        while ((len = srcStream.read(buf)) != -1) {
     130            dstStream.write(buf, 0, len);
     131        }
     132        srcStream.close();
     133        dstStream.close();
     134    }
     135
     136    public static void save(File file, Layer layer) {
     137        if (layer instanceof GpxLayer) {
     138            save(file, (GpxLayer)layer);
     139            ((GpxLayer)layer).data.storageFile = file;
     140        } else if (layer instanceof OsmDataLayer) {
     141            save(file, (OsmDataLayer)layer);
     142        }
     143    }
     144
     145    public static void save(File file, OsmDataLayer layer) {
     146        File tmpFile = null;
     147        try {
     148            if (ExtensionFileFilter.filters[ExtensionFileFilter.GPX].acceptName(file.getPath())) {
     149                GpxExportAction.exportGpx(file, layer);
     150            } else if (ExtensionFileFilter.filters[ExtensionFileFilter.OSM].acceptName(file.getPath())) {
     151                // use a tmp file because if something errors out in the
     152                // process of writing the file, we might just end up with
     153                // a truncated file.  That can destroy lots of work.
     154                if (file.exists()) {
     155                    tmpFile = new File(file.getPath() + "~");
     156                    copy(file, tmpFile);
     157                }
     158                OsmWriter.output(new FileOutputStream(file), new OsmWriter.All(layer.data, false));
     159                if (!Main.pref.getBoolean("save.keepbackup") && (tmpFile != null))
     160                    tmpFile.delete();
     161            } else {
     162                JOptionPane.showMessageDialog(Main.parent, tr("Unknown file extension."));
     163                return;
     164            }
     165            layer.cleanData(null, false);
     166        } catch (IOException e) {
     167            e.printStackTrace();
     168            JOptionPane.showMessageDialog(Main.parent, tr("An error occurred while saving.")+"\n"+e.getMessage());
     169
     170            try {
     171                // if the file save failed, then the tempfile will not
     172                // be deleted.  So, restore the backup if we made one.
     173                if (tmpFile != null && tmpFile.exists()) {
     174                    copy(tmpFile, file);
     175                }
     176            } catch (IOException e2) {
     177                e2.printStackTrace();
     178                JOptionPane.showMessageDialog(Main.parent, tr("An error occurred while restoring backup file.")+"\n"+e2.getMessage());
     179            }
     180        }
     181    }
     182
     183    public static void save(File file, GpxLayer layer) {
     184        File tmpFile = null;
     185        try {
     186            if (ExtensionFileFilter.filters[ExtensionFileFilter.GPX].acceptName(file.getPath())) {
     187
     188                // use a tmp file because if something errors out in the
     189                // process of writing the file, we might just end up with
     190                // a truncated file.  That can destroy lots of work.
     191                if (file.exists()) {
     192                    tmpFile = new File(file.getPath() + "~");
     193                    copy(file, tmpFile);
     194                }
     195                FileOutputStream fo = new FileOutputStream(file);
     196                new GpxWriter(fo).write(layer.data);
     197                fo.flush();
     198                fo.close();
     199
     200                if (!Main.pref.getBoolean("save.keepbackup") && (tmpFile != null)) {
     201                    tmpFile.delete();
     202                }
     203            } else {
     204                JOptionPane.showMessageDialog(Main.parent, tr("Unknown file extension."));
     205                return;
     206            }
     207        } catch (IOException e) {
     208            e.printStackTrace();
     209            JOptionPane.showMessageDialog(Main.parent, tr("An error occurred while saving.")+"\n"+e.getMessage());
     210        }
     211        try {
     212            // if the file save failed, then the tempfile will not
     213            // be deleted.  So, restore the backup if we made one.
     214            if (tmpFile != null && tmpFile.exists()) {
     215                copy(tmpFile, file);
     216            }
     217        } catch (IOException e) {
     218            e.printStackTrace();
     219            JOptionPane.showMessageDialog(Main.parent, tr("An error occurred while restoring backup file.")+"\n"+e.getMessage());
     220        }
     221    }
     222
     223    /**
     224    * Check the data set if it would be empty on save. It is empty, if it contains
     225    * no objects (after all objects that are created and deleted without being
     226    * transfered to the server have been removed).
     227    *
     228    * @return <code>true</code>, if a save result in an empty data set.
     229    */
     230    private boolean isDataSetEmpty(OsmDataLayer layer) {
     231        for (OsmPrimitive osm : layer.data.allNonDeletedPrimitives())
     232            if (!osm.deleted || osm.id > 0)
     233                return false;
     234        return true;
     235    }
    236236}
  • trunk/src/org/openstreetmap/josm/actions/SaveAsAction.java

    r1084 r1169  
    1717public class SaveAsAction extends SaveActionBase {
    1818
    19         /**
    20         * Construct the action with "Save" as label.
    21         * @param layer Save this layer.
    22         */
    23         public SaveAsAction(Layer layer) {
    24                 super(tr("Save as ..."), "save_as", tr("Save the current data to a new file."),
    25                 Shortcut.registerShortcut("system:saveas", tr("File: {0}", tr("Save as ...")), KeyEvent.VK_S, Shortcut.GROUP_MENU, Shortcut.SHIFT_DEFAULT), layer);
    26         }
     19    /**
     20    * Construct the action with "Save" as label.
     21    * @param layer Save this layer.
     22    */
     23    public SaveAsAction(Layer layer) {
     24        super(tr("Save as ..."), "save_as", tr("Save the current data to a new file."),
     25        Shortcut.registerShortcut("system:saveas", tr("File: {0}", tr("Save as ...")), KeyEvent.VK_S, Shortcut.GROUP_MENU, Shortcut.SHIFT_DEFAULT), layer);
     26    }
    2727
    28         @Override protected File getFile(Layer layer) {
    29                 return openFileDialog(layer);
    30         }
     28    @Override protected File getFile(Layer layer) {
     29        return openFileDialog(layer);
     30    }
    3131}
  • trunk/src/org/openstreetmap/josm/actions/SelectAllAction.java

    r1084 r1169  
    1212public class SelectAllAction extends JosmAction {
    1313
    14         public SelectAllAction() {
    15                 super(tr("Select All"),"selectall", tr("Select all undeleted objects in the data layer. This selects incomplete objects too."),
    16                 Shortcut.registerShortcut("system:selectall", tr("Edit: {0}", tr("Select All")), KeyEvent.VK_A, Shortcut.GROUP_MENU), true);
    17         }
     14    public SelectAllAction() {
     15        super(tr("Select All"),"selectall", tr("Select all undeleted objects in the data layer. This selects incomplete objects too."),
     16        Shortcut.registerShortcut("system:selectall", tr("Edit: {0}", tr("Select All")), KeyEvent.VK_A, Shortcut.GROUP_MENU), true);
     17    }
    1818
    19         public void actionPerformed(ActionEvent e) {
    20                 Main.ds.setSelected(Main.ds.allNonDeletedPhysicalPrimitives());
    21         }
     19    public void actionPerformed(ActionEvent e) {
     20        Main.ds.setSelected(Main.ds.allNonDeletedPhysicalPrimitives());
     21    }
    2222}
  • trunk/src/org/openstreetmap/josm/actions/SplitWayAction.java

    r1084 r1169  
    4444public class SplitWayAction extends JosmAction implements SelectionChangedListener {
    4545
    46         private Way selectedWay;
    47         private List<Node> selectedNodes;
    48 
    49         /**
    50         * Create a new SplitWayAction.
    51         */
    52         public SplitWayAction() {
    53                 super(tr("Split Way"), "splitway", tr("Split a way at the selected node."),
    54                 Shortcut.registerShortcut("tools:splitway", tr("Tool: {0}", tr("Split Way")), KeyEvent.VK_P, Shortcut.GROUP_EDIT), true);
    55                 DataSet.selListeners.add(this);
    56         }
    57 
    58         /**
    59         * Called when the action is executed.
    60         *
    61         * This method performs an expensive check whether the selection clearly defines one
    62         * of the split actions outlined above, and if yes, calls the splitWay method.
    63         */
    64         public void actionPerformed(ActionEvent e) {
    65 
    66                 Collection<OsmPrimitive> selection = Main.ds.getSelected();
    67 
    68                 if (!checkSelection(selection)) {
    69                         JOptionPane.showMessageDialog(Main.parent, tr("The current selection cannot be used for splitting."));
    70                         return;
    71                 }
    72 
    73                 selectedWay = null;
    74                 selectedNodes = null;
    75 
    76                 Visitor splitVisitor = new Visitor(){
    77                         public void visit(Node n) {
    78                                 if (selectedNodes == null)
    79                                         selectedNodes = new LinkedList<Node>();
    80                                 selectedNodes.add(n);
    81             }
    82                         public void visit(Way w) {
    83                                 selectedWay = w;
    84             }
    85                         public void visit(Relation e) {
    86                                 // enties are not considered
    87                         }
    88                 };
    89 
    90                 for (OsmPrimitive p : selection)
    91                         p.visit(splitVisitor);
    92 
    93                 // If only nodes are selected, try to guess which way to split. This works if there
    94                 // is exactly one way that all nodes are part of.
    95                 if (selectedWay == null && selectedNodes != null) {
    96                         HashMap<Way, Integer> wayOccurenceCounter = new HashMap<Way, Integer>();
    97                         for (Node n : selectedNodes) {
    98                                 for (Way w : Main.ds.ways) {
    99                                         if (w.deleted || w.incomplete) continue;
    100                                         int last = w.nodes.size()-1;
    101                                         if(last <= 0) continue; // zero or one node ways
    102                                         Boolean circular = w.nodes.get(0).equals(w.nodes.get(last));
    103                                         int i = 0;
    104                                         for (Node wn : w.nodes) {
    105                                                 if ((circular || (i > 0 && i < last)) && n.equals(wn)) {
    106                                                         Integer old = wayOccurenceCounter.get(w);
    107                                                         wayOccurenceCounter.put(w, (old == null) ? 1 : old+1);
    108                                                         break;
    109                                                 }
    110                                                 i++;
    111                                         }
    112                                 }
    113                         }
    114                         if (wayOccurenceCounter.isEmpty()) {
    115                                 JOptionPane.showMessageDialog(Main.parent,
    116                                                 trn("The selected node is no inner part of any way.",
    117                                                                 "The selected nodes are no inner part of any way.", selectedNodes.size()));
    118                                 return;
    119                         }
    120 
    121                         for (Entry<Way, Integer> entry : wayOccurenceCounter.entrySet()) {
    122                                 if (entry.getValue().equals(selectedNodes.size())) {
    123                                         if (selectedWay != null) {
    124                                                 JOptionPane.showMessageDialog(Main.parent, tr("There is more than one way using the node(s) you selected. Please select the way also."));
    125                                                 return;
    126                                         }
    127                                         selectedWay = entry.getKey();
    128                                 }
    129                         }
    130 
    131                         if (selectedWay == null) {
    132                                 JOptionPane.showMessageDialog(Main.parent, tr("The selected nodes do not share the same way."));
    133                                 return;
    134                         }
    135 
    136                 // If a way and nodes are selected, verify that the nodes are part of the way.
    137                 } else if (selectedWay != null && selectedNodes != null) {
    138 
    139                         HashSet<Node> nds = new HashSet<Node>(selectedNodes);
    140                         for (Node n : selectedWay.nodes) {
    141                                 nds.remove(n);
    142                         }
    143                         if (!nds.isEmpty()) {
    144                                 JOptionPane.showMessageDialog(Main.parent,
    145                                                 trn("The selected way does not contain the selected node.",
    146                                                                 "The selected way does not contain all the selected nodes.", selectedNodes.size()));
    147                                 return;
    148                         }
    149                 }
    150 
    151                 // and then do the work.
    152                 splitWay();
    153         }
    154 
    155         /**
    156         * Checks if the selection consists of something we can work with.
    157         * Checks only if the number and type of items selected looks good;
    158         * does not check whether the selected items are really a valid
    159         * input for splitting (this would be too expensive to be carried
    160         * out from the selectionChanged listener).
    161         */
    162         private boolean checkSelection(Collection<? extends OsmPrimitive> selection) {
    163                 boolean way = false;
    164                 boolean node = false;
    165                 for (OsmPrimitive p : selection) {
    166                         if (p instanceof Way && !way) {
    167                                 way = true;
    168                         } else if (p instanceof Node) {
    169                                 node = true;
    170                         } else {
    171                                 return false;
    172                 }
    173                 }
    174                 return node;
    175         }
    176 
    177         /**
    178         * Split a way into two or more parts, starting at a selected node.
    179         */
    180         private void splitWay() {
    181                 // We take our way's list of nodes and copy them to a way chunk (a
    182                 // list of nodes).  Whenever we stumble upon a selected node, we start
    183                 // a new way chunk.
    184 
    185                 Set<Node> nodeSet = new HashSet<Node>(selectedNodes);
    186                 List<List<Node>> wayChunks = new LinkedList<List<Node>>();
    187                 List<Node> currentWayChunk = new ArrayList<Node>();
    188                 wayChunks.add(currentWayChunk);
    189 
    190                 Iterator<Node> it = selectedWay.nodes.iterator();
    191                 while (it.hasNext()) {
    192                         Node currentNode = it.next();
    193                         boolean atEndOfWay = currentWayChunk.isEmpty() || !it.hasNext();
    194                         currentWayChunk.add(currentNode);
    195                         if (nodeSet.contains(currentNode) && !atEndOfWay) {
    196                                 currentWayChunk = new ArrayList<Node>();
    197                                 currentWayChunk.add(currentNode);
    198                                 wayChunks.add(currentWayChunk);
    199                         }
    200                 }
    201 
    202                 // Handle circular ways specially.
    203                 // If you split at a circular way at two nodes, you just want to split
    204                 // it at these points, not also at the former endpoint.
    205                 // So if the last node is the same first node, join the last and the
    206                 // first way chunk.
    207                 List<Node> lastWayChunk = wayChunks.get(wayChunks.size() - 1);
    208                 if (wayChunks.size() >= 2
    209                                 && wayChunks.get(0).get(0) == lastWayChunk.get(lastWayChunk.size() - 1)
    210                                 && !nodeSet.contains(wayChunks.get(0).get(0))) {
    211                         if (wayChunks.size() == 2) {
    212                                 JOptionPane.showMessageDialog(Main.parent, tr("You must select two or more nodes to split a circular way."));
    213                                 return;
    214                         }
    215                         lastWayChunk.remove(lastWayChunk.size() - 1);
    216                         lastWayChunk.addAll(wayChunks.get(0));
    217                         wayChunks.remove(wayChunks.size() - 1);
    218                         wayChunks.set(0, lastWayChunk);
    219                 }
    220 
    221                 if (wayChunks.size() < 2) {
    222                         if(wayChunks.get(0).get(0) == wayChunks.get(0).get(wayChunks.get(0).size()-1))
    223                                 JOptionPane.showMessageDialog(Main.parent, tr("You must select two or more nodes to split a circular way."));
    224                         else
    225                                 JOptionPane.showMessageDialog(Main.parent, tr("The way cannot be split at the selected nodes. (Hint: Select nodes in the middle of the way.)"));
    226                         return;
    227                 }
    228                 //Main.debug("wayChunks.size(): " + wayChunks.size());
    229                 //Main.debug("way id: " + selectedWay.id);
    230 
    231                 // build a list of commands, and also a new selection list
    232                 Collection<Command> commandList = new ArrayList<Command>(wayChunks.size());
    233                 Collection<Way> newSelection = new ArrayList<Way>(wayChunks.size());
    234 
    235                 Iterator<List<Node>> chunkIt = wayChunks.iterator();
    236 
    237                 // First, change the original way
    238                 Way changedWay = new Way(selectedWay);
    239                 changedWay.nodes.clear();
    240                 changedWay.nodes.addAll(chunkIt.next());
    241                 commandList.add(new ChangeCommand(selectedWay, changedWay));
    242                 newSelection.add(selectedWay);
    243 
    244                 Collection<Way> newWays = new ArrayList<Way>();
    245                 // Second, create new ways
    246                 while (chunkIt.hasNext()) {
    247                         Way wayToAdd = new Way();
    248                         if (selectedWay.keys != null) {
    249                                 wayToAdd.keys = new HashMap<String, String>(selectedWay.keys);
    250                                 wayToAdd.checkTagged();
     46    private Way selectedWay;
     47    private List<Node> selectedNodes;
     48
     49    /**
     50    * Create a new SplitWayAction.
     51    */
     52    public SplitWayAction() {
     53        super(tr("Split Way"), "splitway", tr("Split a way at the selected node."),
     54        Shortcut.registerShortcut("tools:splitway", tr("Tool: {0}", tr("Split Way")), KeyEvent.VK_P, Shortcut.GROUP_EDIT), true);
     55        DataSet.selListeners.add(this);
     56    }
     57
     58    /**
     59    * Called when the action is executed.
     60    *
     61    * This method performs an expensive check whether the selection clearly defines one
     62    * of the split actions outlined above, and if yes, calls the splitWay method.
     63    */
     64    public void actionPerformed(ActionEvent e) {
     65
     66        Collection<OsmPrimitive> selection = Main.ds.getSelected();
     67
     68        if (!checkSelection(selection)) {
     69            JOptionPane.showMessageDialog(Main.parent, tr("The current selection cannot be used for splitting."));
     70            return;
     71        }
     72
     73        selectedWay = null;
     74        selectedNodes = null;
     75
     76        Visitor splitVisitor = new Visitor(){
     77            public void visit(Node n) {
     78                if (selectedNodes == null)
     79                    selectedNodes = new LinkedList<Node>();
     80                selectedNodes.add(n);
     81            }
     82            public void visit(Way w) {
     83                selectedWay = w;
     84            }
     85            public void visit(Relation e) {
     86                // enties are not considered
     87            }
     88        };
     89
     90        for (OsmPrimitive p : selection)
     91            p.visit(splitVisitor);
     92
     93        // If only nodes are selected, try to guess which way to split. This works if there
     94        // is exactly one way that all nodes are part of.
     95        if (selectedWay == null && selectedNodes != null) {
     96            HashMap<Way, Integer> wayOccurenceCounter = new HashMap<Way, Integer>();
     97            for (Node n : selectedNodes) {
     98                for (Way w : Main.ds.ways) {
     99                    if (w.deleted || w.incomplete) continue;
     100                    int last = w.nodes.size()-1;
     101                    if(last <= 0) continue; // zero or one node ways
     102                    Boolean circular = w.nodes.get(0).equals(w.nodes.get(last));
     103                    int i = 0;
     104                    for (Node wn : w.nodes) {
     105                        if ((circular || (i > 0 && i < last)) && n.equals(wn)) {
     106                            Integer old = wayOccurenceCounter.get(w);
     107                            wayOccurenceCounter.put(w, (old == null) ? 1 : old+1);
     108                            break;
     109                        }
     110                        i++;
     111                    }
     112                }
     113            }
     114            if (wayOccurenceCounter.isEmpty()) {
     115                JOptionPane.showMessageDialog(Main.parent,
     116                        trn("The selected node is no inner part of any way.",
     117                                "The selected nodes are no inner part of any way.", selectedNodes.size()));
     118                return;
     119            }
     120
     121            for (Entry<Way, Integer> entry : wayOccurenceCounter.entrySet()) {
     122                if (entry.getValue().equals(selectedNodes.size())) {
     123                    if (selectedWay != null) {
     124                        JOptionPane.showMessageDialog(Main.parent, tr("There is more than one way using the node(s) you selected. Please select the way also."));
     125                        return;
     126                    }
     127                    selectedWay = entry.getKey();
     128                }
     129            }
     130
     131            if (selectedWay == null) {
     132                JOptionPane.showMessageDialog(Main.parent, tr("The selected nodes do not share the same way."));
     133                return;
     134            }
     135
     136        // If a way and nodes are selected, verify that the nodes are part of the way.
     137        } else if (selectedWay != null && selectedNodes != null) {
     138
     139            HashSet<Node> nds = new HashSet<Node>(selectedNodes);
     140            for (Node n : selectedWay.nodes) {
     141                nds.remove(n);
     142            }
     143            if (!nds.isEmpty()) {
     144                JOptionPane.showMessageDialog(Main.parent,
     145                        trn("The selected way does not contain the selected node.",
     146                                "The selected way does not contain all the selected nodes.", selectedNodes.size()));
     147                return;
     148            }
     149        }
     150
     151        // and then do the work.
     152        splitWay();
     153    }
     154
     155    /**
     156    * Checks if the selection consists of something we can work with.
     157    * Checks only if the number and type of items selected looks good;
     158    * does not check whether the selected items are really a valid
     159    * input for splitting (this would be too expensive to be carried
     160    * out from the selectionChanged listener).
     161    */
     162    private boolean checkSelection(Collection<? extends OsmPrimitive> selection) {
     163        boolean way = false;
     164        boolean node = false;
     165        for (OsmPrimitive p : selection) {
     166            if (p instanceof Way && !way) {
     167                way = true;
     168            } else if (p instanceof Node) {
     169                node = true;
     170            } else {
     171                return false;
     172        }
     173        }
     174        return node;
     175    }
     176
     177    /**
     178    * Split a way into two or more parts, starting at a selected node.
     179    */
     180    private void splitWay() {
     181        // We take our way's list of nodes and copy them to a way chunk (a
     182        // list of nodes).  Whenever we stumble upon a selected node, we start
     183        // a new way chunk.
     184
     185        Set<Node> nodeSet = new HashSet<Node>(selectedNodes);
     186        List<List<Node>> wayChunks = new LinkedList<List<Node>>();
     187        List<Node> currentWayChunk = new ArrayList<Node>();
     188        wayChunks.add(currentWayChunk);
     189
     190        Iterator<Node> it = selectedWay.nodes.iterator();
     191        while (it.hasNext()) {
     192            Node currentNode = it.next();
     193            boolean atEndOfWay = currentWayChunk.isEmpty() || !it.hasNext();
     194            currentWayChunk.add(currentNode);
     195            if (nodeSet.contains(currentNode) && !atEndOfWay) {
     196                currentWayChunk = new ArrayList<Node>();
     197                currentWayChunk.add(currentNode);
     198                wayChunks.add(currentWayChunk);
     199            }
     200        }
     201
     202        // Handle circular ways specially.
     203        // If you split at a circular way at two nodes, you just want to split
     204        // it at these points, not also at the former endpoint.
     205        // So if the last node is the same first node, join the last and the
     206        // first way chunk.
     207        List<Node> lastWayChunk = wayChunks.get(wayChunks.size() - 1);
     208        if (wayChunks.size() >= 2
     209                && wayChunks.get(0).get(0) == lastWayChunk.get(lastWayChunk.size() - 1)
     210                && !nodeSet.contains(wayChunks.get(0).get(0))) {
     211            if (wayChunks.size() == 2) {
     212                JOptionPane.showMessageDialog(Main.parent, tr("You must select two or more nodes to split a circular way."));
     213                return;
     214            }
     215            lastWayChunk.remove(lastWayChunk.size() - 1);
     216            lastWayChunk.addAll(wayChunks.get(0));
     217            wayChunks.remove(wayChunks.size() - 1);
     218            wayChunks.set(0, lastWayChunk);
     219        }
     220
     221        if (wayChunks.size() < 2) {
     222            if(wayChunks.get(0).get(0) == wayChunks.get(0).get(wayChunks.get(0).size()-1))
     223                JOptionPane.showMessageDialog(Main.parent, tr("You must select two or more nodes to split a circular way."));
     224            else
     225                JOptionPane.showMessageDialog(Main.parent, tr("The way cannot be split at the selected nodes. (Hint: Select nodes in the middle of the way.)"));
     226            return;
     227        }
     228        //Main.debug("wayChunks.size(): " + wayChunks.size());
     229        //Main.debug("way id: " + selectedWay.id);
     230
     231        // build a list of commands, and also a new selection list
     232        Collection<Command> commandList = new ArrayList<Command>(wayChunks.size());
     233        Collection<Way> newSelection = new ArrayList<Way>(wayChunks.size());
     234
     235        Iterator<List<Node>> chunkIt = wayChunks.iterator();
     236
     237        // First, change the original way
     238        Way changedWay = new Way(selectedWay);
     239        changedWay.nodes.clear();
     240        changedWay.nodes.addAll(chunkIt.next());
     241        commandList.add(new ChangeCommand(selectedWay, changedWay));
     242        newSelection.add(selectedWay);
     243
     244        Collection<Way> newWays = new ArrayList<Way>();
     245        // Second, create new ways
     246        while (chunkIt.hasNext()) {
     247            Way wayToAdd = new Way();
     248            if (selectedWay.keys != null) {
     249                wayToAdd.keys = new HashMap<String, String>(selectedWay.keys);
     250                wayToAdd.checkTagged();
    251251                                wayToAdd.checkDirectionTagged();
    252                         }
    253                         newWays.add(wayToAdd);
    254                         wayToAdd.nodes.addAll(chunkIt.next());
    255                         commandList.add(new AddCommand(wayToAdd));
    256                         //Main.debug("wayToAdd: " + wayToAdd);
    257                         newSelection.add(wayToAdd);
    258 
    259                 }
    260                 Boolean warnme=false;
    261                 // now copy all relations to new way also
    262                 for (Relation r : Main.ds.relations) {
    263                         if (r.deleted || r.incomplete) continue;
    264                         for (RelationMember rm : r.members) {
    265                                 if (rm.member instanceof Way) {
    266                                         if (rm.member == selectedWay)
    267                                         {
    268                                                 Relation c = new Relation(r);
    269                                                 for(Way wayToAdd : newWays)
    270                                                 {
    271                                                         RelationMember em = new RelationMember();
    272                                                         em.member = wayToAdd;
    273                                                         em.role = rm.role;
    274                                                         if(em.role.length() > 0)
    275                                                                 warnme = true;
    276                                                         c.members.add(em);
    277                                                 }
    278                                                 commandList.add(new ChangeCommand(r, c));
    279                                                 break;
    280                                         }
    281                                 }
    282                         }
    283                 }
    284                 if(warnme)
    285                         JOptionPane.showMessageDialog(Main.parent, tr("A role based relation membership was copied to all new ways.\nYou should verify this and correct it when necessary."));
    286 
    287                 NameVisitor v = new NameVisitor();
    288                 v.visit(selectedWay);
    289                 Main.main.undoRedo.add(
    290                         new SequenceCommand(tr("Split way {0} into {1} parts",
    291                                 v.name, wayChunks.size()),
    292                         commandList));
    293                 Main.ds.setSelected(newSelection);
    294         }
    295 
    296         /**
    297         * Enable the "split way" menu option if the selection looks like we could use it.
    298         */
    299         public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) {
    300                 setEnabled(checkSelection(newSelection));
    301         }
     252            }
     253            newWays.add(wayToAdd);
     254            wayToAdd.nodes.addAll(chunkIt.next());
     255            commandList.add(new AddCommand(wayToAdd));
     256            //Main.debug("wayToAdd: " + wayToAdd);
     257            newSelection.add(wayToAdd);
     258
     259        }
     260        Boolean warnme=false;
     261        // now copy all relations to new way also
     262        for (Relation r : Main.ds.relations) {
     263            if (r.deleted || r.incomplete) continue;
     264            for (RelationMember rm : r.members) {
     265                if (rm.member instanceof Way) {
     266                    if (rm.member == selectedWay)
     267                    {
     268                        Relation c = new Relation(r);
     269                        for(Way wayToAdd : newWays)
     270                        {
     271                            RelationMember em = new RelationMember();
     272                            em.member = wayToAdd;
     273                            em.role = rm.role;
     274                            if(em.role.length() > 0)
     275                                warnme = true;
     276                            c.members.add(em);
     277                        }
     278                        commandList.add(new ChangeCommand(r, c));
     279                        break;
     280                    }
     281                }
     282            }
     283        }
     284        if(warnme)
     285            JOptionPane.showMessageDialog(Main.parent, tr("A role based relation membership was copied to all new ways.\nYou should verify this and correct it when necessary."));
     286
     287        NameVisitor v = new NameVisitor();
     288        v.visit(selectedWay);
     289        Main.main.undoRedo.add(
     290            new SequenceCommand(tr("Split way {0} into {1} parts",
     291                v.name, wayChunks.size()),
     292            commandList));
     293        Main.ds.setSelected(newSelection);
     294    }
     295
     296    /**
     297    * Enable the "split way" menu option if the selection looks like we could use it.
     298    */
     299    public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) {
     300        setEnabled(checkSelection(newSelection));
     301    }
    302302}
  • trunk/src/org/openstreetmap/josm/actions/ToggleGPXLinesAction.java

    r1138 r1169  
    1212public class ToggleGPXLinesAction extends JosmAction {
    1313
    14         public ToggleGPXLinesAction() {
    15                 super(tr("Toggle GPX Lines"), "gps-lines", tr("Toggles the global setting ''{0}''.", tr("Draw lines between raw gps points.")),
    16                 Shortcut.registerShortcut("view:gpxlines", tr("View: {0}", tr("Toggle GPX Lines")), KeyEvent.VK_X, Shortcut.GROUP_MENU, Shortcut.SHIFT_DEFAULT), true);
    17         }
     14    public ToggleGPXLinesAction() {
     15        super(tr("Toggle GPX Lines"), "gps-lines", tr("Toggles the global setting ''{0}''.", tr("Draw lines between raw gps points.")),
     16        Shortcut.registerShortcut("view:gpxlines", tr("View: {0}", tr("Toggle GPX Lines")), KeyEvent.VK_X, Shortcut.GROUP_MENU, Shortcut.SHIFT_DEFAULT), true);
     17    }
    1818
    19         public void actionPerformed(ActionEvent e) {
    20                 Main.pref.put("draw.rawgps.lines", !Main.pref.getBoolean("draw.rawgps.lines"));
    21                 Main.map.mapView.repaint();
    22         }
     19    public void actionPerformed(ActionEvent e) {
     20        Main.pref.put("draw.rawgps.lines", !Main.pref.getBoolean("draw.rawgps.lines"));
     21        Main.map.mapView.repaint();
     22    }
    2323}
  • trunk/src/org/openstreetmap/josm/actions/UnGlueAction.java

    r1084 r1169  
    3636public class UnGlueAction extends JosmAction { //implements SelectionChangedListener {
    3737
    38         private Node selectedNode;
    39         private Way selectedWay;
    40         private ArrayList<Node> selectedNodes;
    41 
    42         /**
    43         * Create a new UnGlueAction.
    44         */
    45         public UnGlueAction() {
    46                 super(tr("UnGlue Ways"), "unglueways", tr("Duplicate nodes that are used by multiple ways."),
    47                 Shortcut.registerShortcut("tools:unglue", tr("Tool: {0}", tr("UnGlue Ways")), KeyEvent.VK_G, Shortcut.GROUP_EDIT), true);
    48                 //DataSet.selListeners.add(this);
    49         }
    50 
    51         /**
    52         * Called when the action is executed.
    53         *
    54         * This method does some checking on the selection and calls the matching unGlueWay method.
    55         */
    56         public void actionPerformed(ActionEvent e) {
    57 
    58                 Collection<OsmPrimitive> selection = Main.ds.getSelected();
    59 
    60                 if (checkSelection(selection)) {
    61                         int count = 0;
    62                         for (Way w : Main.ds.ways) {
    63                                 if (w.deleted || w.incomplete || w.nodes.size() < 1) continue;
    64                                 if (!w.nodes.contains(selectedNode)) continue;
    65                                 count++;
    66                         }
    67                         if (count < 2) {
    68                                 JOptionPane.showMessageDialog(Main.parent, tr("This node is not glued to anything else."));
    69                         } else {
    70                                 // and then do the work.
    71                                 unglueWays();
    72                         }
    73                 } else if (checkSelection2(selection)) {
    74                         ArrayList<Node> tmpNodes = new ArrayList<Node>();
    75                         for (Node n : selectedNodes) {
    76                                 int count = 0;
    77                                 for (Way w : Main.ds.ways) {
    78                                         if (w.deleted || w.incomplete || w.nodes.size() < 1) continue;
    79                                         if (!w.nodes.contains(n)) continue;
    80                                         count++;
    81                                 }
    82                                 if (count >= 2) {
    83                                         tmpNodes.add(n);
    84                                 }
    85                         }
    86                         if (tmpNodes.size() < 1) {
    87                                 if (selection.size() > 1) {
    88                                         JOptionPane.showMessageDialog(Main.parent, tr("None of these nodes is glued to anything else."));
    89                                 } else {
    90                                         JOptionPane.showMessageDialog(Main.parent, tr("None of this way's nodes is glued to anything else."));
    91                                 }
    92                         } else {
    93                                 // and then do the work.
    94                                 selectedNodes = tmpNodes;
    95                                 unglueWays2();
    96                         }
    97                 } else {
    98                         JOptionPane.showMessageDialog(Main.parent,
    99                                 tr("The current selection cannot be used for unglueing.")+"\n"+
    100                                 "\n"+
    101                                 tr("Select either:")+"\n"+
    102                                 tr("* One node that is used by more than one way, or")+"\n"+
    103                                 tr("* One node that is used by more than one way and one of those ways, or")+"\n"+
    104                                 tr("* One way that has one or more nodes that are used by more than one way, or")+"\n"+
    105                                 tr("* One way and one or more of its nodes that are used by more than one way.")+"\n"+
    106                                 "\n"+
    107                                 tr("Note: If a way is selected, this way will get fresh copies of the unglued\n"+
    108                                    "nodes and the new nodes will be selected. Otherwise, all ways will get their\n"+
    109                                    "own copy and all nodes will be selected.")
    110                         );
    111                 }
    112                 selectedNode = null;
    113                 selectedWay = null;
    114                 selectedNodes = null;
    115         }
    116 
    117         /**
    118         * Checks if the selection consists of something we can work with.
    119         * Checks only if the number and type of items selected looks good;
    120         * does not check whether the selected items are really a valid
    121         * input for splitting (this would be too expensive to be carried
    122         * out from the selectionChanged listener).
    123         *
    124         * If this method returns "true", selectedNode and selectedWay will
    125         * be set.
    126         *
    127         * Returns true if either one node is selected or one node and one
    128         * way are selected and the node is part of the way.
    129         *
    130         * The way will be put into the object variable "selectedWay", the
    131         * node into "selectedNode".
    132         */
    133         private boolean checkSelection(Collection<? extends OsmPrimitive> selection) {
    134 
    135                 int size = selection.size();
    136                 if (size < 1 || size > 2)
    137                         return false;
    138 
    139                 selectedNode = null;
    140                 selectedWay = null;
    141 
    142                 for (OsmPrimitive p : selection) {
    143                         if (p instanceof Node) {
    144                                 selectedNode = (Node) p;
    145                                 if (size == 1 || selectedWay != null)
    146                                         return size == 1 || selectedWay.nodes.contains(selectedNode);
    147                         } else if (p instanceof Way) {
    148                                 selectedWay = (Way) p;
    149                                 if (size == 2 && selectedNode != null)
    150                                         return selectedWay.nodes.contains(selectedNode);
    151                         }
    152                 }
    153 
    154                 return false;
    155         }
    156 
    157         /**
    158         * Checks if the selection consists of something we can work with.
    159         * Checks only if the number and type of items selected looks good;
    160         * does not check whether the selected items are really a valid
    161         * input for splitting (this would be too expensive to be carried
    162         * out from the selectionChanged listener).
    163         *
    164         * Returns true if one way and any number of nodes that are part of
    165         * that way are selected. Note: "any" can be none, then all nodes of
    166         * the way are used.
    167         *
    168         * The way will be put into the object variable "selectedWay", the
    169         * nodes into "selectedNodes".
    170         */
    171         private boolean checkSelection2(Collection<? extends OsmPrimitive> selection) {
    172                 if (selection.size() < 1)
    173                         return false;
    174 
    175                 selectedWay = null;
    176                 for (OsmPrimitive p : selection) {
    177                         if (p instanceof Way) {
    178                                 if (selectedWay != null) {
    179                                         return false;
    180                                 }
    181                                 selectedWay = (Way) p;
    182                         }
    183                 }
    184                 if (selectedWay == null) {
    185                         return false;
    186                 }
    187 
    188                 selectedNodes = new ArrayList<Node>();
    189                 for (OsmPrimitive p : selection) {
    190                         if (p instanceof Node) {
    191                                 Node n = (Node) p;
    192                                 if (!selectedWay.nodes.contains(n)) {
    193                                         return false;
    194                                 }
    195                                 selectedNodes.add(n);
    196                         }
    197                 }
    198 
    199                 if (selectedNodes.size() < 1) {
    200                         selectedNodes.addAll(selectedWay.nodes);
    201                 }
    202 
    203                 return true;
    204         }
    205 
    206         /**
    207         * dupe the given node of the given way
    208         *
    209         * -> the new node will be put into the parameter newNodes.
    210         * -> the add-node command will be put into the parameter cmds.
    211         * -> the changed way will be returned and must be put into cmds by the caller!
    212         */
    213         private Way modifyWay(Node originalNode, Way w, List<Command> cmds, List<Node> newNodes) {
    214                 ArrayList<Node> nn = new ArrayList<Node>();
    215                 for (Node pushNode : w.nodes) {
    216                         if (originalNode == pushNode) {
    217                                 // clone the node for all other ways
    218                                 pushNode = new Node(pushNode);
    219                                 pushNode.id = 0;
    220                                 newNodes.add(pushNode);
    221                                 cmds.add(new AddCommand(pushNode));
    222                         }
    223                         nn.add(pushNode);
    224                 }
    225                 Way newWay = new Way(w);
    226                 newWay.nodes.clear();
    227                 newWay.nodes.addAll(nn);
    228 
    229                 return newWay;
    230         }
    231 
    232         /**
    233         * put all newNodes into the same relation(s) that originalNode is in
    234         */
    235         private void fixRelations(Node originalNode, List<Command> cmds, List<Node> newNodes) {
    236                 // modify all relations containing the node
    237                 Relation newRel = null;
    238                 HashSet<String> rolesToReAdd = null;
    239                 for (Relation r : Main.ds.relations) {
    240                         if (r.deleted || r.incomplete) continue;
    241                         newRel = null;
    242                         rolesToReAdd = null;
    243                         for (RelationMember rm : r.members) {
    244                                 if (rm.member instanceof Node) {
    245                                         if (rm.member == originalNode) {
    246                                                 if (newRel == null) {
    247                                                         newRel = new Relation(r);
    248                                                         newRel.members.clear();
    249                                                         rolesToReAdd = new HashSet<String>();
    250                                                 }
    251                                                 rolesToReAdd.add(rm.role);
    252                                         }
    253                                 }
    254                         }
    255                         if (newRel != null) {
    256                                 for (RelationMember rm : r.members) {
    257                                         //if (rm.member != selectedNode) {
    258                                                 newRel.members.add(rm);
    259                                         //}
    260                                 }
    261                                 for (Node n : newNodes) {
    262                                         for (String role : rolesToReAdd) {
    263                                                 newRel.members.add(new RelationMember(role, n));
    264                                         }
    265                                 }
    266                                 cmds.add(new ChangeCommand(r, newRel));
    267                         }
    268                 }
    269         }
    270 
    271 
    272         /**
    273         * dupe a single node into as many nodes as there are ways using it, OR
    274         *
    275         * dupe a single node once, and put the copy on the selected way
    276         */
    277         private void unglueWays() {
    278                 LinkedList<Command> cmds = new LinkedList<Command>();
    279                 List<Node> newNodes = new LinkedList<Node>();
    280 
    281                 if (selectedWay == null) {
    282                         boolean firstway = true;
    283                         // modify all ways containing the nodes
    284                         for (Way w : Main.ds.ways) {
    285                                 if (w.deleted || w.incomplete || w.nodes.size() < 1) continue;
    286                                 if (!w.nodes.contains(selectedNode)) continue;
    287                                 if (!firstway) cmds.add(new ChangeCommand(w, modifyWay(selectedNode, w, cmds, newNodes)));
    288                                 firstway = false;
    289                         }
    290                 } else {
    291                         cmds.add(new ChangeCommand(selectedWay, modifyWay(selectedNode, selectedWay, cmds, newNodes)));
    292                 }
    293 
    294                 fixRelations(selectedNode, cmds, newNodes);
    295 
    296                 Main.main.undoRedo.add(new SequenceCommand(tr("Dupe into {0} nodes", newNodes.size()+1), cmds));
    297                 if (selectedWay == null) { // if a node has been selected, new selection is ALL nodes
    298                         newNodes.add(selectedNode);
    299                 } // if a node and a way has been selected, new selection is only the new node that was added to the selected way
    300                 Main.ds.setSelected(newNodes);
    301         }
    302 
    303         /**
    304         * dupe all nodes that are selected, and put the copies on the selected way
    305         *
    306         */
    307         private void unglueWays2() {
    308                 LinkedList<Command> cmds = new LinkedList<Command>();
    309                 List<Node> allNewNodes = new LinkedList<Node>();
    310                 Way tmpWay = selectedWay;
    311 
    312                 for (Node n : selectedNodes) {
    313                         List<Node> newNodes = new LinkedList<Node>();
    314                         tmpWay = modifyWay(n, tmpWay, cmds, newNodes);
    315                         fixRelations(n, cmds, newNodes);
    316                         allNewNodes.addAll(newNodes);
    317                 }
    318                 cmds.add(new ChangeCommand(selectedWay, tmpWay)); // only one changeCommand for a way, else garbage will happen
    319 
    320                 Main.main.undoRedo.add(new SequenceCommand(tr("Dupe {0} nodes into {1} nodes", selectedNodes.size(), selectedNodes.size()+allNewNodes.size()), cmds));
    321                 Main.ds.setSelected(allNewNodes);
    322         }
     38    private Node selectedNode;
     39    private Way selectedWay;
     40    private ArrayList<Node> selectedNodes;
     41
     42    /**
     43    * Create a new UnGlueAction.
     44    */
     45    public UnGlueAction() {
     46        super(tr("UnGlue Ways"), "unglueways", tr("Duplicate nodes that are used by multiple ways."),
     47        Shortcut.registerShortcut("tools:unglue", tr("Tool: {0}", tr("UnGlue Ways")), KeyEvent.VK_G, Shortcut.GROUP_EDIT), true);
     48        //DataSet.selListeners.add(this);
     49    }
     50
     51    /**
     52    * Called when the action is executed.
     53    *
     54    * This method does some checking on the selection and calls the matching unGlueWay method.
     55    */
     56    public void actionPerformed(ActionEvent e) {
     57
     58        Collection<OsmPrimitive> selection = Main.ds.getSelected();
     59
     60        if (checkSelection(selection)) {
     61            int count = 0;
     62            for (Way w : Main.ds.ways) {
     63                if (w.deleted || w.incomplete || w.nodes.size() < 1) continue;
     64                if (!w.nodes.contains(selectedNode)) continue;
     65                count++;
     66            }
     67            if (count < 2) {
     68                JOptionPane.showMessageDialog(Main.parent, tr("This node is not glued to anything else."));
     69            } else {
     70                // and then do the work.
     71                unglueWays();
     72            }
     73        } else if (checkSelection2(selection)) {
     74            ArrayList<Node> tmpNodes = new ArrayList<Node>();
     75            for (Node n : selectedNodes) {
     76                int count = 0;
     77                for (Way w : Main.ds.ways) {
     78                    if (w.deleted || w.incomplete || w.nodes.size() < 1) continue;
     79                    if (!w.nodes.contains(n)) continue;
     80                    count++;
     81                }
     82                if (count >= 2) {
     83                    tmpNodes.add(n);
     84                }
     85            }
     86            if (tmpNodes.size() < 1) {
     87                if (selection.size() > 1) {
     88                    JOptionPane.showMessageDialog(Main.parent, tr("None of these nodes is glued to anything else."));
     89                } else {
     90                    JOptionPane.showMessageDialog(Main.parent, tr("None of this way's nodes is glued to anything else."));
     91                }
     92            } else {
     93                // and then do the work.
     94                selectedNodes = tmpNodes;
     95                unglueWays2();
     96            }
     97        } else {
     98            JOptionPane.showMessageDialog(Main.parent,
     99                tr("The current selection cannot be used for unglueing.")+"\n"+
     100                "\n"+
     101                tr("Select either:")+"\n"+
     102                tr("* One node that is used by more than one way, or")+"\n"+
     103                tr("* One node that is used by more than one way and one of those ways, or")+"\n"+
     104                tr("* One way that has one or more nodes that are used by more than one way, or")+"\n"+
     105                tr("* One way and one or more of its nodes that are used by more than one way.")+"\n"+
     106                "\n"+
     107                tr("Note: If a way is selected, this way will get fresh copies of the unglued\n"+
     108                   "nodes and the new nodes will be selected. Otherwise, all ways will get their\n"+
     109                   "own copy and all nodes will be selected.")
     110            );
     111        }
     112        selectedNode = null;
     113        selectedWay = null;
     114        selectedNodes = null;
     115    }
     116
     117    /**
     118    * Checks if the selection consists of something we can work with.
     119    * Checks only if the number and type of items selected looks good;
     120    * does not check whether the selected items are really a valid
     121    * input for splitting (this would be too expensive to be carried
     122    * out from the selectionChanged listener).
     123    *
     124    * If this method returns "true", selectedNode and selectedWay will
     125    * be set.
     126    *
     127    * Returns true if either one node is selected or one node and one
     128    * way are selected and the node is part of the way.
     129    *
     130    * The way will be put into the object variable "selectedWay", the
     131    * node into "selectedNode".
     132    */
     133    private boolean checkSelection(Collection<? extends OsmPrimitive> selection) {
     134
     135        int size = selection.size();
     136        if (size < 1 || size > 2)
     137            return false;
     138
     139        selectedNode = null;
     140        selectedWay = null;
     141
     142        for (OsmPrimitive p : selection) {
     143            if (p instanceof Node) {
     144                selectedNode = (Node) p;
     145                if (size == 1 || selectedWay != null)
     146                    return size == 1 || selectedWay.nodes.contains(selectedNode);
     147            } else if (p instanceof Way) {
     148                selectedWay = (Way) p;
     149                if (size == 2 && selectedNode != null)
     150                    return selectedWay.nodes.contains(selectedNode);
     151            }
     152        }
     153
     154        return false;
     155    }
     156
     157    /**
     158    * Checks if the selection consists of something we can work with.
     159    * Checks only if the number and type of items selected looks good;
     160    * does not check whether the selected items are really a valid
     161    * input for splitting (this would be too expensive to be carried
     162    * out from the selectionChanged listener).
     163    *
     164    * Returns true if one way and any number of nodes that are part of
     165    * that way are selected. Note: "any" can be none, then all nodes of
     166    * the way are used.
     167    *
     168    * The way will be put into the object variable "selectedWay", the
     169    * nodes into "selectedNodes".
     170    */
     171    private boolean checkSelection2(Collection<? extends OsmPrimitive> selection) {
     172        if (selection.size() < 1)
     173            return false;
     174
     175        selectedWay = null;
     176        for (OsmPrimitive p : selection) {
     177            if (p instanceof Way) {
     178                if (selectedWay != null) {
     179                    return false;
     180                }
     181                selectedWay = (Way) p;
     182            }
     183        }
     184        if (selectedWay == null) {
     185            return false;
     186        }
     187
     188        selectedNodes = new ArrayList<Node>();
     189        for (OsmPrimitive p : selection) {
     190            if (p instanceof Node) {
     191                Node n = (Node) p;
     192                if (!selectedWay.nodes.contains(n)) {
     193                    return false;
     194                }
     195                selectedNodes.add(n);
     196            }
     197        }
     198
     199        if (selectedNodes.size() < 1) {
     200            selectedNodes.addAll(selectedWay.nodes);
     201        }
     202
     203        return true;
     204    }
     205
     206    /**
     207    * dupe the given node of the given way
     208    *
     209    * -> the new node will be put into the parameter newNodes.
     210    * -> the add-node command will be put into the parameter cmds.
     211    * -> the changed way will be returned and must be put into cmds by the caller!
     212    */
     213    private Way modifyWay(Node originalNode, Way w, List<Command> cmds, List<Node> newNodes) {
     214        ArrayList<Node> nn = new ArrayList<Node>();
     215        for (Node pushNode : w.nodes) {
     216            if (originalNode == pushNode) {
     217                // clone the node for all other ways
     218                pushNode = new Node(pushNode);
     219                pushNode.id = 0;
     220                newNodes.add(pushNode);
     221                cmds.add(new AddCommand(pushNode));
     222            }
     223            nn.add(pushNode);
     224        }
     225        Way newWay = new Way(w);
     226        newWay.nodes.clear();
     227        newWay.nodes.addAll(nn);
     228
     229        return newWay;
     230    }
     231
     232    /**
     233    * put all newNodes into the same relation(s) that originalNode is in
     234    */
     235    private void fixRelations(Node originalNode, List<Command> cmds, List<Node> newNodes) {
     236        // modify all relations containing the node
     237        Relation newRel = null;
     238        HashSet<String> rolesToReAdd = null;
     239        for (Relation r : Main.ds.relations) {
     240            if (r.deleted || r.incomplete) continue;
     241            newRel = null;
     242            rolesToReAdd = null;
     243            for (RelationMember rm : r.members) {
     244                if (rm.member instanceof Node) {
     245                    if (rm.member == originalNode) {
     246                        if (newRel == null) {
     247                            newRel = new Relation(r);
     248                            newRel.members.clear();
     249                            rolesToReAdd = new HashSet<String>();
     250                        }
     251                        rolesToReAdd.add(rm.role);
     252                    }
     253                }
     254            }
     255            if (newRel != null) {
     256                for (RelationMember rm : r.members) {
     257                    //if (rm.member != selectedNode) {
     258                        newRel.members.add(rm);
     259                    //}
     260                }
     261                for (Node n : newNodes) {
     262                    for (String role : rolesToReAdd) {
     263                        newRel.members.add(new RelationMember(role, n));
     264                    }
     265                }
     266                cmds.add(new ChangeCommand(r, newRel));
     267            }
     268        }
     269    }
     270
     271
     272    /**
     273    * dupe a single node into as many nodes as there are ways using it, OR
     274    *
     275    * dupe a single node once, and put the copy on the selected way
     276    */
     277    private void unglueWays() {
     278        LinkedList<Command> cmds = new LinkedList<Command>();
     279        List<Node> newNodes = new LinkedList<Node>();
     280
     281        if (selectedWay == null) {
     282            boolean firstway = true;
     283            // modify all ways containing the nodes
     284            for (Way w : Main.ds.ways) {
     285                if (w.deleted || w.incomplete || w.nodes.size() < 1) continue;
     286                if (!w.nodes.contains(selectedNode)) continue;
     287                if (!firstway) cmds.add(new ChangeCommand(w, modifyWay(selectedNode, w, cmds, newNodes)));
     288                firstway = false;
     289            }
     290        } else {
     291            cmds.add(new ChangeCommand(selectedWay, modifyWay(selectedNode, selectedWay, cmds, newNodes)));
     292        }
     293
     294        fixRelations(selectedNode, cmds, newNodes);
     295
     296        Main.main.undoRedo.add(new SequenceCommand(tr("Dupe into {0} nodes", newNodes.size()+1), cmds));
     297        if (selectedWay == null) { // if a node has been selected, new selection is ALL nodes
     298            newNodes.add(selectedNode);
     299        } // if a node and a way has been selected, new selection is only the new node that was added to the selected way
     300        Main.ds.setSelected(newNodes);
     301    }
     302
     303    /**
     304    * dupe all nodes that are selected, and put the copies on the selected way
     305    *
     306    */
     307    private void unglueWays2() {
     308        LinkedList<Command> cmds = new LinkedList<Command>();
     309        List<Node> allNewNodes = new LinkedList<Node>();
     310        Way tmpWay = selectedWay;
     311
     312        for (Node n : selectedNodes) {
     313            List<Node> newNodes = new LinkedList<Node>();
     314            tmpWay = modifyWay(n, tmpWay, cmds, newNodes);
     315            fixRelations(n, cmds, newNodes);
     316            allNewNodes.addAll(newNodes);
     317        }
     318        cmds.add(new ChangeCommand(selectedWay, tmpWay)); // only one changeCommand for a way, else garbage will happen
     319
     320        Main.main.undoRedo.add(new SequenceCommand(tr("Dupe {0} nodes into {1} nodes", selectedNodes.size(), selectedNodes.size()+allNewNodes.size()), cmds));
     321        Main.ds.setSelected(allNewNodes);
     322    }
    323323
    324324// Disabled because we have such a nice help text that would not be shown otherwise.
    325325//
    326 //      /**
    327 //      * Enable the menu option if the selection looks like we could use it.
    328 //      */
    329 //      public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) {
    330 //              setEnabled(checkSelection(newSelection) || checkSelection2(newSelection));
    331 //              selectedNode = null;
    332 //              selectedWay = null;
    333 //              selectedNodes = null;
    334 //      }
     326//  /**
     327//  * Enable the menu option if the selection looks like we could use it.
     328//  */
     329//  public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) {
     330//      setEnabled(checkSelection(newSelection) || checkSelection2(newSelection));
     331//      selectedNode = null;
     332//      selectedWay = null;
     333//      selectedNodes = null;
     334//  }
    335335}
  • trunk/src/org/openstreetmap/josm/actions/UndoAction.java

    r1084 r1169  
    1717public class UndoAction extends JosmAction {
    1818
    19         /**
    20         * Construct the action with "Undo" as label.
    21         */
    22         public UndoAction() {
    23                 super(tr("Undo"), "undo", tr("Undo the last action."),
    24                 Shortcut.registerShortcut("system:undo", tr("Edit: {0}", tr("Undo")), KeyEvent.VK_Z, Shortcut.GROUP_MENU), true);
    25                 setEnabled(false);
    26         }
     19    /**
     20    * Construct the action with "Undo" as label.
     21    */
     22    public UndoAction() {
     23        super(tr("Undo"), "undo", tr("Undo the last action."),
     24        Shortcut.registerShortcut("system:undo", tr("Edit: {0}", tr("Undo")), KeyEvent.VK_Z, Shortcut.GROUP_MENU), true);
     25        setEnabled(false);
     26    }
    2727
    28         public void actionPerformed(ActionEvent e) {
    29                 if (Main.map == null)
    30                         return;
    31                 Main.map.repaint();
    32                 Main.main.undoRedo.undo();
    33         }
     28    public void actionPerformed(ActionEvent e) {
     29        if (Main.map == null)
     30            return;
     31        Main.map.repaint();
     32        Main.main.undoRedo.undo();
     33    }
    3434}
  • trunk/src/org/openstreetmap/josm/actions/UnselectAllAction.java

    r1084 r1169  
    1313public class UnselectAllAction extends JosmAction {
    1414
    15         public UnselectAllAction() {
    16                 super(tr("Unselect All"), "unselectall", tr("Unselect all objects."),
    17                 Shortcut.registerShortcut("edit:unselectall", tr("Edit: {0}", tr("Unselect All")), KeyEvent.VK_U, Shortcut.GROUP_EDIT), true);
    18                 // this is not really GROUP_EDIT, but users really would complain if the yhad to reconfigure because we put
    19                 // the correct group in
     15    public UnselectAllAction() {
     16        super(tr("Unselect All"), "unselectall", tr("Unselect all objects."),
     17        Shortcut.registerShortcut("edit:unselectall", tr("Edit: {0}", tr("Unselect All")), KeyEvent.VK_U, Shortcut.GROUP_EDIT), true);
     18        // this is not really GROUP_EDIT, but users really would complain if the yhad to reconfigure because we put
     19        // the correct group in
    2020
    21                 // Add extra shortcut C-S-a
    22                 Main.contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
    23                 Shortcut.registerShortcut("edit:unselectallfocus", tr("Edit: {0}", tr("Unselect All (Focus)")),
    24                 KeyEvent.VK_A, Shortcut.GROUP_MENU, Shortcut.SHIFT_DEFAULT).getKeyStroke(),
    25                 tr("Unselect All"));
     21        // Add extra shortcut C-S-a
     22        Main.contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
     23        Shortcut.registerShortcut("edit:unselectallfocus", tr("Edit: {0}", tr("Unselect All (Focus)")),
     24        KeyEvent.VK_A, Shortcut.GROUP_MENU, Shortcut.SHIFT_DEFAULT).getKeyStroke(),
     25        tr("Unselect All"));
    2626
    27                 // Add extra shortcut ESCAPE
    28                 /*
    29                 * FIXME: this isn't optimal. In a better world the mapmode actions
    30                 * would be able to capture keyboard events and react accordingly. But
    31                 * for now this is a reasonable approximation.
    32                 */
    33                 Main.contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
    34                 Shortcut.registerShortcut("edit:unselectallescape", tr("Edit: {0}", tr("Unselect All (Escape)")),
    35                 KeyEvent.VK_ESCAPE, Shortcut.GROUP_DIRECT).getKeyStroke(),
    36                 tr("Unselect All"));
    37         }
     27        // Add extra shortcut ESCAPE
     28        /*
     29        * FIXME: this isn't optimal. In a better world the mapmode actions
     30        * would be able to capture keyboard events and react accordingly. But
     31        * for now this is a reasonable approximation.
     32        */
     33        Main.contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
     34        Shortcut.registerShortcut("edit:unselectallescape", tr("Edit: {0}", tr("Unselect All (Escape)")),
     35        KeyEvent.VK_ESCAPE, Shortcut.GROUP_DIRECT).getKeyStroke(),
     36        tr("Unselect All"));
     37    }
    3838
    39         public void actionPerformed(ActionEvent e) {
    40                 Main.ds.setSelected();
    41         }
     39    public void actionPerformed(ActionEvent e) {
     40        Main.ds.setSelected();
     41    }
    4242}
  • trunk/src/org/openstreetmap/josm/actions/UploadAction.java

    r1084 r1169  
    3535public class UploadAction extends JosmAction {
    3636
    37         /** Upload Hook */
    38         public interface UploadHook {
    39                 /**
    40                 * Checks the upload.
    41                 * @param add The added primitives
    42                 * @param update The updated primitives
    43                 * @param delete The deleted primitives
    44                 * @return true, if the upload can continue
    45                 */
    46                 public boolean checkUpload(Collection<OsmPrimitive> add, Collection<OsmPrimitive> update, Collection<OsmPrimitive> delete);
    47         }
     37    /** Upload Hook */
     38    public interface UploadHook {
     39        /**
     40        * Checks the upload.
     41        * @param add The added primitives
     42        * @param update The updated primitives
     43        * @param delete The deleted primitives
     44        * @return true, if the upload can continue
     45        */
     46        public boolean checkUpload(Collection<OsmPrimitive> add, Collection<OsmPrimitive> update, Collection<OsmPrimitive> delete);
     47    }
    4848
    49         /**
    50         * The list of upload hooks. These hooks will be called one after the other
    51         * when the user wants to upload data. Plugins can insert their own hooks here
    52         * if they want to be able to veto an upload.
    53         *
    54         * Be default, the standard upload dialog is the only element in the list.
    55         * Plugins should normally insert their code before that, so that the upload
    56         * dialog is the last thing shown before upload really starts; on occasion
    57         * however, a plugin might also want to insert something after that.
    58         */
    59         public final LinkedList<UploadHook> uploadHooks = new LinkedList<UploadHook>();
     49    /**
     50    * The list of upload hooks. These hooks will be called one after the other
     51    * when the user wants to upload data. Plugins can insert their own hooks here
     52    * if they want to be able to veto an upload.
     53    *
     54    * Be default, the standard upload dialog is the only element in the list.
     55    * Plugins should normally insert their code before that, so that the upload
     56    * dialog is the last thing shown before upload really starts; on occasion
     57    * however, a plugin might also want to insert something after that.
     58    */
     59    public final LinkedList<UploadHook> uploadHooks = new LinkedList<UploadHook>();
    6060
    61         public UploadAction() {
    62                 super(tr("Upload to OSM ..."), "upload", tr("Upload all changes to the OSM server."),
    63                 Shortcut.registerShortcut("file:upload", tr("File: {0}", tr("Upload to OSM ...")), KeyEvent.VK_U, Shortcut.GROUPS_ALT1+Shortcut.GROUP_HOTKEY), true);
     61    public UploadAction() {
     62        super(tr("Upload to OSM ..."), "upload", tr("Upload all changes to the OSM server."),
     63        Shortcut.registerShortcut("file:upload", tr("File: {0}", tr("Upload to OSM ...")), KeyEvent.VK_U, Shortcut.GROUPS_ALT1+Shortcut.GROUP_HOTKEY), true);
    6464
    65                 /**
    66                 * Displays a screen where the actions that would be taken are displayed and
    67                 * give the user the possibility to cancel the upload.
    68                 */
    69                 uploadHooks.add(new UploadHook() {
    70                         public boolean checkUpload(Collection<OsmPrimitive> add, Collection<OsmPrimitive> update, Collection<OsmPrimitive> delete) {
     65        /**
     66        * Displays a screen where the actions that would be taken are displayed and
     67        * give the user the possibility to cancel the upload.
     68        */
     69        uploadHooks.add(new UploadHook() {
     70            public boolean checkUpload(Collection<OsmPrimitive> add, Collection<OsmPrimitive> update, Collection<OsmPrimitive> delete) {
    7171
    72                                 JPanel p = new JPanel(new GridBagLayout());
     72                JPanel p = new JPanel(new GridBagLayout());
    7373
    74                                 OsmPrimitivRenderer renderer = new OsmPrimitivRenderer();
     74                OsmPrimitivRenderer renderer = new OsmPrimitivRenderer();
    7575
    76                                 if (!add.isEmpty()) {
    77                                         p.add(new JLabel(tr("Objects to add:")), GBC.eol());
    78                                         JList l = new JList(add.toArray());
    79                                         l.setCellRenderer(renderer);
    80                                         l.setVisibleRowCount(l.getModel().getSize() < 6 ? l.getModel().getSize() : 10);
    81                                         p.add(new JScrollPane(l), GBC.eol().fill());
    82                                 }
     76                if (!add.isEmpty()) {
     77                    p.add(new JLabel(tr("Objects to add:")), GBC.eol());
     78                    JList l = new JList(add.toArray());
     79                    l.setCellRenderer(renderer);
     80                    l.setVisibleRowCount(l.getModel().getSize() < 6 ? l.getModel().getSize() : 10);
     81                    p.add(new JScrollPane(l), GBC.eol().fill());
     82                }
    8383
    84                                 if (!update.isEmpty()) {
    85                                         p.add(new JLabel(tr("Objects to modify:")), GBC.eol());
    86                                         JList l = new JList(update.toArray());
    87                                         l.setCellRenderer(renderer);
    88                                         l.setVisibleRowCount(l.getModel().getSize() < 6 ? l.getModel().getSize() : 10);
    89                                         p.add(new JScrollPane(l), GBC.eol().fill());
    90                                 }
     84                if (!update.isEmpty()) {
     85                    p.add(new JLabel(tr("Objects to modify:")), GBC.eol());
     86                    JList l = new JList(update.toArray());
     87                    l.setCellRenderer(renderer);
     88                    l.setVisibleRowCount(l.getModel().getSize() < 6 ? l.getModel().getSize() : 10);
     89                    p.add(new JScrollPane(l), GBC.eol().fill());
     90                }
    9191
    92                                 if (!delete.isEmpty()) {
    93                                         p.add(new JLabel(tr("Objects to delete:")), GBC.eol());
    94                                         JList l = new JList(delete.toArray());
    95                                         l.setCellRenderer(renderer);
    96                                         l.setVisibleRowCount(l.getModel().getSize() < 6 ? l.getModel().getSize() : 10);
    97                                         p.add(new JScrollPane(l), GBC.eol().fill());
    98                                 }
     92                if (!delete.isEmpty()) {
     93                    p.add(new JLabel(tr("Objects to delete:")), GBC.eol());
     94                    JList l = new JList(delete.toArray());
     95                    l.setCellRenderer(renderer);
     96                    l.setVisibleRowCount(l.getModel().getSize() < 6 ? l.getModel().getSize() : 10);
     97                    p.add(new JScrollPane(l), GBC.eol().fill());
     98                }
    9999
    100                                 return JOptionPane.showConfirmDialog(Main.parent, p, tr("Upload these changes?"),
    101                                                 JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION;
    102                         }
    103                 });
    104         }
     100                return JOptionPane.showConfirmDialog(Main.parent, p, tr("Upload these changes?"),
     101                        JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION;
     102            }
     103        });
     104    }
    105105
    106         public void actionPerformed(ActionEvent e) {
    107                 if (Main.map == null) {
    108                         JOptionPane.showMessageDialog(Main.parent,tr("Nothing to upload. Get some data first."));
    109                         return;
    110                 }
     106    public void actionPerformed(ActionEvent e) {
     107        if (Main.map == null) {
     108            JOptionPane.showMessageDialog(Main.parent,tr("Nothing to upload. Get some data first."));
     109            return;
     110        }
    111111
    112                 if (!Main.map.conflictDialog.conflicts.isEmpty()) {
    113                         JOptionPane.showMessageDialog(Main.parent,tr("There are unresolved conflicts. You have to resolve these first."));
    114                         Main.map.conflictDialog.action.button.setSelected(true);
    115                         Main.map.conflictDialog.action.actionPerformed(null);
    116                         return;
    117                 }
     112        if (!Main.map.conflictDialog.conflicts.isEmpty()) {
     113            JOptionPane.showMessageDialog(Main.parent,tr("There are unresolved conflicts. You have to resolve these first."));
     114            Main.map.conflictDialog.action.button.setSelected(true);
     115            Main.map.conflictDialog.action.actionPerformed(null);
     116            return;
     117        }
    118118
    119                 final LinkedList<OsmPrimitive> add = new LinkedList<OsmPrimitive>();
    120                 final LinkedList<OsmPrimitive> update = new LinkedList<OsmPrimitive>();
    121                 final LinkedList<OsmPrimitive> delete = new LinkedList<OsmPrimitive>();
    122                 for (OsmPrimitive osm : Main.ds.allPrimitives()) {
    123                         if (osm.get("josm/ignore") != null)
    124                                 continue;
    125                         if (osm.id == 0 && !osm.deleted)
    126                                 add.addLast(osm);
    127                         else if (osm.modified && !osm.deleted)
    128                                 update.addLast(osm);
    129                         else if (osm.deleted && osm.id != 0)
    130                                 delete.addFirst(osm);
    131                 }
     119        final LinkedList<OsmPrimitive> add = new LinkedList<OsmPrimitive>();
     120        final LinkedList<OsmPrimitive> update = new LinkedList<OsmPrimitive>();
     121        final LinkedList<OsmPrimitive> delete = new LinkedList<OsmPrimitive>();
     122        for (OsmPrimitive osm : Main.ds.allPrimitives()) {
     123            if (osm.get("josm/ignore") != null)
     124                continue;
     125            if (osm.id == 0 && !osm.deleted)
     126                add.addLast(osm);
     127            else if (osm.modified && !osm.deleted)
     128                update.addLast(osm);
     129            else if (osm.deleted && osm.id != 0)
     130                delete.addFirst(osm);
     131        }
    132132
    133                 if (add.isEmpty() && update.isEmpty() && delete.isEmpty()) {
    134                         JOptionPane.showMessageDialog(Main.parent,tr("No changes to upload."));
    135                         return;
    136                 }
     133        if (add.isEmpty() && update.isEmpty() && delete.isEmpty()) {
     134            JOptionPane.showMessageDialog(Main.parent,tr("No changes to upload."));
     135            return;
     136        }
    137137
    138                 // Call all upload hooks in sequence. The upload confirmation dialog
    139                 // is one of these.
    140                 for(UploadHook hook : uploadHooks)
    141                         if(!hook.checkUpload(add, update, delete))
    142                                 return;
     138        // Call all upload hooks in sequence. The upload confirmation dialog
     139        // is one of these.
     140        for(UploadHook hook : uploadHooks)
     141            if(!hook.checkUpload(add, update, delete))
     142                return;
    143143
    144                 final OsmServerWriter server = new OsmServerWriter();
    145                 final Collection<OsmPrimitive> all = new LinkedList<OsmPrimitive>();
    146                 all.addAll(add);
    147                 all.addAll(update);
    148                 all.addAll(delete);
     144        final OsmServerWriter server = new OsmServerWriter();
     145        final Collection<OsmPrimitive> all = new LinkedList<OsmPrimitive>();
     146        all.addAll(add);
     147        all.addAll(update);
     148        all.addAll(delete);
    149149
    150                 PleaseWaitRunnable uploadTask = new PleaseWaitRunnable(tr("Uploading data")){
    151                         @Override protected void realRun() throws SAXException {
    152                                 server.uploadOsm(all);
    153                         }
    154                         @Override protected void finish() {
    155                                 Main.main.editLayer().cleanData(server.processed, !add.isEmpty());
    156                         }
    157                         @Override protected void cancel() {
    158                                 server.cancel();
    159                         }
    160                 };
    161                 Main.worker.execute(uploadTask);
    162         }
     150        PleaseWaitRunnable uploadTask = new PleaseWaitRunnable(tr("Uploading data")){
     151            @Override protected void realRun() throws SAXException {
     152                server.uploadOsm(all);
     153            }
     154            @Override protected void finish() {
     155                Main.main.editLayer().cleanData(server.processed, !add.isEmpty());
     156            }
     157            @Override protected void cancel() {
     158                server.cancel();
     159            }
     160        };
     161        Main.worker.execute(uploadTask);
     162    }
    163163}
  • trunk/src/org/openstreetmap/josm/actions/ZoomInAction.java

    r1087 r1169  
    1212public final class ZoomInAction extends JosmAction {
    1313
    14         public ZoomInAction() {
    15                 super(tr("Zoom in"), "dialogs/zoomin", tr("Zoom in"),
    16                 Shortcut.registerShortcut("view:zoomin", tr("View: {0}", tr("Zoom in")), KeyEvent.VK_PLUS, Shortcut.GROUP_DIRECT), true);
    17                 setEnabled(true);
    18         }
     14    public ZoomInAction() {
     15        super(tr("Zoom in"), "dialogs/zoomin", tr("Zoom in"),
     16        Shortcut.registerShortcut("view:zoomin", tr("View: {0}", tr("Zoom in")), KeyEvent.VK_PLUS, Shortcut.GROUP_DIRECT), true);
     17        setEnabled(true);
     18    }
    1919
    20         public void actionPerformed(ActionEvent e) {
     20    public void actionPerformed(ActionEvent e) {
    2121        if (Main.map == null) return;
    22                 double zoom = Main.map.mapView.getScale();
    23                 Main.map.mapView.zoomTo(Main.map.mapView.getCenter(), zoom * .9);
    24         }
     22        double zoom = Main.map.mapView.getScale();
     23        Main.map.mapView.zoomTo(Main.map.mapView.getCenter(), zoom * .9);
     24    }
    2525}
  • trunk/src/org/openstreetmap/josm/actions/ZoomOutAction.java

    r1087 r1169  
    1212public final class ZoomOutAction extends JosmAction {
    1313
    14         public ZoomOutAction() {
    15                 super(tr("Zoom out"), "dialogs/zoomout", tr("Zoom out"),
    16                 Shortcut.registerShortcut("view:zoomout", tr("View: {0}", tr("Zoom out")), KeyEvent.VK_MINUS, Shortcut.GROUP_DIRECT), true);
    17                 setEnabled(true);
    18         }
     14    public ZoomOutAction() {
     15        super(tr("Zoom out"), "dialogs/zoomout", tr("Zoom out"),
     16        Shortcut.registerShortcut("view:zoomout", tr("View: {0}", tr("Zoom out")), KeyEvent.VK_MINUS, Shortcut.GROUP_DIRECT), true);
     17        setEnabled(true);
     18    }
    1919
    20         public void actionPerformed(ActionEvent e) {
     20    public void actionPerformed(ActionEvent e) {
    2121        if (Main.map == null) return;
    22                 double zoom = Main.map.mapView.getScale();
    23                 Main.map.mapView.zoomTo(Main.map.mapView.getCenter(), zoom /.9);
    24         }
     22        double zoom = Main.map.mapView.getScale();
     23        Main.map.mapView.zoomTo(Main.map.mapView.getCenter(), zoom /.9);
     24    }
    2525}
  • trunk/src/org/openstreetmap/josm/actions/audio/AudioBackAction.java

    r1084 r1169  
    1515public class AudioBackAction extends JosmAction {
    1616
    17         private double amount; // note, normally negative, i.e. jump backwards in time
     17    private double amount; // note, normally negative, i.e. jump backwards in time
    1818
    19         public AudioBackAction() {
    20                 super(tr("Back"), "audio-back", tr("Jump back."),
    21                 Shortcut.registerShortcut("audio:back", tr("Audio: {0}", tr("Back")), KeyEvent.VK_F6, Shortcut.GROUP_DIRECT), true);
    22                 try {
    23                         amount = - Double.parseDouble(Main.pref.get("audio.forwardbackamount","10.0"));
    24                 } catch (NumberFormatException e) {
    25                         amount = 10.0;
    26                 }
    27                 this.putValue("help", "Action/Back");
    28         }
     19    public AudioBackAction() {
     20        super(tr("Back"), "audio-back", tr("Jump back."),
     21        Shortcut.registerShortcut("audio:back", tr("Audio: {0}", tr("Back")), KeyEvent.VK_F6, Shortcut.GROUP_DIRECT), true);
     22        try {
     23            amount = - Double.parseDouble(Main.pref.get("audio.forwardbackamount","10.0"));
     24        } catch (NumberFormatException e) {
     25            amount = 10.0;
     26        }
     27        this.putValue("help", "Action/Back");
     28    }
    2929
    30         public void actionPerformed(ActionEvent e) {
    31                 try {
    32                         if (AudioPlayer.playing() || AudioPlayer.paused())
    33                                 AudioPlayer.play(AudioPlayer.url(), AudioPlayer.position() + amount);
    34                         else
    35                                 MarkerLayer.playAudio();
    36                 } catch (Exception ex) {
    37                         AudioPlayer.audioMalfunction(ex);
    38                 }
    39         }
     30    public void actionPerformed(ActionEvent e) {
     31        try {
     32            if (AudioPlayer.playing() || AudioPlayer.paused())
     33                AudioPlayer.play(AudioPlayer.url(), AudioPlayer.position() + amount);
     34            else
     35                MarkerLayer.playAudio();
     36        } catch (Exception ex) {
     37            AudioPlayer.audioMalfunction(ex);
     38        }
     39    }
    4040}
  • trunk/src/org/openstreetmap/josm/actions/audio/AudioFastSlowAction.java

    r1084 r1169  
    1111abstract public class AudioFastSlowAction extends JosmAction {
    1212
    13         private double multiplier;
     13    private double multiplier;
    1414
    15         public AudioFastSlowAction(String name, String iconName, String tooltip, Shortcut shortcut, boolean fast) {
    16                 super(name, iconName, tooltip, shortcut, true);
    17                 try {
    18                         multiplier = Double.parseDouble(Main.pref.get("audio.fastfwdmultiplier","1.3"));
    19                 } catch (NumberFormatException e) {
    20                         multiplier = 1.3;
    21                 }
    22                 if (! fast)
    23                         multiplier = 1.0 / multiplier;
    24         }
     15    public AudioFastSlowAction(String name, String iconName, String tooltip, Shortcut shortcut, boolean fast) {
     16        super(name, iconName, tooltip, shortcut, true);
     17        try {
     18            multiplier = Double.parseDouble(Main.pref.get("audio.fastfwdmultiplier","1.3"));
     19        } catch (NumberFormatException e) {
     20            multiplier = 1.3;
     21        }
     22        if (! fast)
     23            multiplier = 1.0 / multiplier;
     24    }
    2525
    26         @Deprecated
    27         public AudioFastSlowAction(String name, String iconName, String tooltip, int shortcut, int modifier, boolean fast) {
    28                 super(name, iconName, tooltip, shortcut, modifier, true);
    29                 try {
    30                         multiplier = Double.parseDouble(Main.pref.get("audio.fastfwdmultiplier","1.3"));
    31                 } catch (NumberFormatException e) {
    32                         multiplier = 1.3;
    33                 }
    34                 if (! fast)
    35                         multiplier = 1.0 / multiplier;
    36         }
     26    @Deprecated
     27    public AudioFastSlowAction(String name, String iconName, String tooltip, int shortcut, int modifier, boolean fast) {
     28        super(name, iconName, tooltip, shortcut, modifier, true);
     29        try {
     30            multiplier = Double.parseDouble(Main.pref.get("audio.fastfwdmultiplier","1.3"));
     31        } catch (NumberFormatException e) {
     32            multiplier = 1.3;
     33        }
     34        if (! fast)
     35            multiplier = 1.0 / multiplier;
     36    }
    3737
    38         public void actionPerformed(ActionEvent e) {
    39                 double speed = AudioPlayer.speed();
    40                 if (speed * multiplier <= 0.1)
    41                         return;
    42                 try {
    43                         if (AudioPlayer.playing() || AudioPlayer.paused())
    44                                 AudioPlayer.play(AudioPlayer.url(), AudioPlayer.position(), speed * multiplier);
    45                 } catch (Exception ex) {
    46                         AudioPlayer.audioMalfunction(ex);
    47                 }
    48         }
     38    public void actionPerformed(ActionEvent e) {
     39        double speed = AudioPlayer.speed();
     40        if (speed * multiplier <= 0.1)
     41            return;
     42        try {
     43            if (AudioPlayer.playing() || AudioPlayer.paused())
     44                AudioPlayer.play(AudioPlayer.url(), AudioPlayer.position(), speed * multiplier);
     45        } catch (Exception ex) {
     46            AudioPlayer.audioMalfunction(ex);
     47        }
     48    }
    4949}
  • trunk/src/org/openstreetmap/josm/actions/audio/AudioFasterAction.java

    r1084 r1169  
    99public class AudioFasterAction extends AudioFastSlowAction {
    1010
    11         public AudioFasterAction() {
    12                 super(tr("Faster"), "audio-faster", tr("Faster Forward"),
    13                 Shortcut.registerShortcut("audio:faster", tr("Audio: {0}", tr("Faster")), KeyEvent.VK_F9, Shortcut.GROUP_DIRECT), true);
    14         }
     11    public AudioFasterAction() {
     12        super(tr("Faster"), "audio-faster", tr("Faster Forward"),
     13        Shortcut.registerShortcut("audio:faster", tr("Audio: {0}", tr("Faster")), KeyEvent.VK_F9, Shortcut.GROUP_DIRECT), true);
     14    }
    1515}
  • trunk/src/org/openstreetmap/josm/actions/audio/AudioFwdAction.java

    r1084 r1169  
    1515public class AudioFwdAction extends JosmAction {
    1616
    17         private double amount;
     17    private double amount;
    1818
    19         public AudioFwdAction() {
    20                 super(tr("Forward"), "audio-fwd", tr("Jump forward"),
    21                 Shortcut.registerShortcut("audio:forward", tr("Audio: {0}", tr("Forward")), KeyEvent.VK_F7, Shortcut.GROUP_DIRECT), true);
    22                 try {
    23                         amount = Double.parseDouble(Main.pref.get("audio.forwardbackamount","10.0"));
    24                 } catch (NumberFormatException e) {
    25                         amount = 10.0;
    26                 }
    27         }
     19    public AudioFwdAction() {
     20        super(tr("Forward"), "audio-fwd", tr("Jump forward"),
     21        Shortcut.registerShortcut("audio:forward", tr("Audio: {0}", tr("Forward")), KeyEvent.VK_F7, Shortcut.GROUP_DIRECT), true);
     22        try {
     23            amount = Double.parseDouble(Main.pref.get("audio.forwardbackamount","10.0"));
     24        } catch (NumberFormatException e) {
     25            amount = 10.0;
     26        }
     27    }
    2828
    29         public void actionPerformed(ActionEvent e) {
    30                 try {
    31                         if (AudioPlayer.playing() || AudioPlayer.paused())
    32                                 AudioPlayer.play(AudioPlayer.url(), AudioPlayer.position() + amount);
    33                         else
    34                                 MarkerLayer.playAudio();
    35                 } catch (Exception ex) {
    36                         AudioPlayer.audioMalfunction(ex);
    37                 }
    38         }
     29    public void actionPerformed(ActionEvent e) {
     30        try {
     31            if (AudioPlayer.playing() || AudioPlayer.paused())
     32                AudioPlayer.play(AudioPlayer.url(), AudioPlayer.position() + amount);
     33            else
     34                MarkerLayer.playAudio();
     35        } catch (Exception ex) {
     36            AudioPlayer.audioMalfunction(ex);
     37        }
     38    }
    3939}
  • trunk/src/org/openstreetmap/josm/actions/audio/AudioNextAction.java

    r1084 r1169  
    1313public class AudioNextAction extends JosmAction {
    1414
    15         public AudioNextAction() {
    16                 super(tr("Next Marker"), "audio-next", tr("Play next marker."),
    17                 Shortcut.registerShortcut("audio:next", tr("Audio: {0}", tr("Next Marker")), KeyEvent.VK_F8, Shortcut.GROUP_DIRECT), true);
    18         }
     15    public AudioNextAction() {
     16        super(tr("Next Marker"), "audio-next", tr("Play next marker."),
     17        Shortcut.registerShortcut("audio:next", tr("Audio: {0}", tr("Next Marker")), KeyEvent.VK_F8, Shortcut.GROUP_DIRECT), true);
     18    }
    1919
    20         public void actionPerformed(ActionEvent e) {
    21                 MarkerLayer.playNextMarker();
    22         }
     20    public void actionPerformed(ActionEvent e) {
     21        MarkerLayer.playNextMarker();
     22    }
    2323}
  • trunk/src/org/openstreetmap/josm/actions/audio/AudioPlayPauseAction.java

    r1084 r1169  
    1515public class AudioPlayPauseAction extends JosmAction {
    1616
    17         public AudioPlayPauseAction() {
    18                 super(tr("Play/pause"), "audio-playpause", tr("Play/pause audio."),
    19                 Shortcut.registerShortcut("audio:pause", tr("Audio: {0}", tr("Play/pause")), KeyEvent.VK_PERIOD, Shortcut.GROUP_DIRECT), true);
    20         }
     17    public AudioPlayPauseAction() {
     18        super(tr("Play/pause"), "audio-playpause", tr("Play/pause audio."),
     19        Shortcut.registerShortcut("audio:pause", tr("Audio: {0}", tr("Play/pause")), KeyEvent.VK_PERIOD, Shortcut.GROUP_DIRECT), true);
     20    }
    2121
    22         public void actionPerformed(ActionEvent e) {
    23                 URL url = AudioPlayer.url();
    24                 try {
    25                         if (AudioPlayer.paused() && url != null) {
    26                                 AudioPlayer.play(url);
    27                         } else if (AudioPlayer.playing()){
    28                                 if (AudioPlayer.speed() != 1.0)
    29                                         AudioPlayer.play(url, AudioPlayer.position());
    30                                 else
    31                                         AudioPlayer.pause();
    32                         } else {
    33                                 // find first audio marker to play
    34                                 MarkerLayer.playAudio();
    35                         }
    36                 } catch (Exception ex) {
    37                         AudioPlayer.audioMalfunction(ex);
    38                 }
    39         }
     22    public void actionPerformed(ActionEvent e) {
     23        URL url = AudioPlayer.url();
     24        try {
     25            if (AudioPlayer.paused() && url != null) {
     26                AudioPlayer.play(url);
     27            } else if (AudioPlayer.playing()){
     28                if (AudioPlayer.speed() != 1.0)
     29                    AudioPlayer.play(url, AudioPlayer.position());
     30                else
     31                    AudioPlayer.pause();
     32            } else {
     33                // find first audio marker to play
     34                MarkerLayer.playAudio();
     35            }
     36        } catch (Exception ex) {
     37            AudioPlayer.audioMalfunction(ex);
     38        }
     39    }
    4040}
  • trunk/src/org/openstreetmap/josm/actions/audio/AudioPrevAction.java

    r1084 r1169  
    1313public class AudioPrevAction extends JosmAction {
    1414
    15         public AudioPrevAction() {
    16                 super(tr("Previous Marker"), "audio-prev", tr("Play previous marker."),
    17                 Shortcut.registerShortcut("audio:prev", tr("Audio: {0}", tr("Previous Marker")), KeyEvent.VK_F5, Shortcut.GROUP_DIRECT), true);
    18         }
     15    public AudioPrevAction() {
     16        super(tr("Previous Marker"), "audio-prev", tr("Play previous marker."),
     17        Shortcut.registerShortcut("audio:prev", tr("Audio: {0}", tr("Previous Marker")), KeyEvent.VK_F5, Shortcut.GROUP_DIRECT), true);
     18    }
    1919
    20         public void actionPerformed(ActionEvent e) {
    21                 MarkerLayer.playPreviousMarker();
    22         }
     20    public void actionPerformed(ActionEvent e) {
     21        MarkerLayer.playPreviousMarker();
     22    }
    2323}
  • trunk/src/org/openstreetmap/josm/actions/audio/AudioSlowerAction.java

    r1084 r1169  
    99public class AudioSlowerAction extends AudioFastSlowAction {
    1010
    11         public AudioSlowerAction() {
    12                 super(tr("Slower"), "audio-slower", tr("Slower Forward"),
    13                 Shortcut.registerShortcut("audio:slower", tr("Audio: {0}", tr("Slower")), KeyEvent.VK_F4, Shortcut.GROUP_DIRECT), true);
    14         }
     11    public AudioSlowerAction() {
     12        super(tr("Slower"), "audio-slower", tr("Slower Forward"),
     13        Shortcut.registerShortcut("audio:slower", tr("Audio: {0}", tr("Slower")), KeyEvent.VK_F4, Shortcut.GROUP_DIRECT), true);
     14    }
    1515}
  • trunk/src/org/openstreetmap/josm/actions/downloadtasks/DownloadGpsTask.java

    r1148 r1169  
    2121public class DownloadGpsTask implements DownloadTask {
    2222
    23         private static class Task extends PleaseWaitRunnable {
    24                 private BoundingBoxDownloader reader;
    25                 private GpxData rawData;
    26                 private final boolean newLayer;
     23    private static class Task extends PleaseWaitRunnable {
     24        private BoundingBoxDownloader reader;
     25        private GpxData rawData;
     26        private final boolean newLayer;
    2727
    28                 public Task(boolean newLayer, BoundingBoxDownloader reader) {
    29                         super(tr("Downloading GPS data"));
    30                         this.reader = reader;
    31                         this.newLayer = newLayer;
    32                 }
     28        public Task(boolean newLayer, BoundingBoxDownloader reader) {
     29            super(tr("Downloading GPS data"));
     30            this.reader = reader;
     31            this.newLayer = newLayer;
     32        }
    3333
    34                 @Override public void realRun() throws IOException, SAXException {
    35                         rawData = reader.parseRawGps();
    36                 }
     34        @Override public void realRun() throws IOException, SAXException {
     35            rawData = reader.parseRawGps();
     36        }
    3737
    38                 @Override protected void finish() {
    39                         if (rawData == null)
    40                                 return;
     38        @Override protected void finish() {
     39            if (rawData == null)
     40                return;
    4141                        rawData.recalculateBounds();
    4242                        Bounds b = rawData.bounds;
    43                                                 String name = b.min.lat() + " " + b.min.lon() + " x " + b.max.lat() + " " + b.max.lon();
    44                                                 GpxLayer layer = new GpxLayer(rawData, name);
    45                         if (newLayer || findMergeLayer() == null)
    46                     Main.main.addLayer(layer);
    47                         else
    48                                 findMergeLayer().mergeFrom(layer);
    49                 }
    50 
    51                 private Layer findMergeLayer() {
    52                         if (Main.map == null)
    53                                 return null;
    54                 Layer active = Main.map.mapView.getActiveLayer();
    55                 if (active != null && active instanceof GpxLayer)
    56                         return active;
    57                 for (Layer l : Main.map.mapView.getAllLayers())
    58                                 if (l instanceof GpxLayer)
    59                                 return l;
    60                 return null;
     43                        String name = b.min.lat() + " " + b.min.lon() + " x " + b.max.lat() + " " + b.max.lon();
     44                        GpxLayer layer = new GpxLayer(rawData, name);
     45            if (newLayer || findMergeLayer() == null)
     46                Main.main.addLayer(layer);
     47            else
     48                findMergeLayer().mergeFrom(layer);
    6149        }
    6250
    63                 @Override protected void cancel() {
    64                         if (reader != null)
    65                                 reader.cancel();
    66                 }
    67         }
     51        private Layer findMergeLayer() {
     52            if (Main.map == null)
     53                return null;
     54            Layer active = Main.map.mapView.getActiveLayer();
     55            if (active != null && active instanceof GpxLayer)
     56                return active;
     57            for (Layer l : Main.map.mapView.getAllLayers())
     58                if (l instanceof GpxLayer)
     59                    return l;
     60            return null;
     61        }
    6862
    69         private JCheckBox checkBox = new JCheckBox(tr("Raw GPS data"));
    70 
    71         public void download(DownloadAction action, double minlat, double minlon, double maxlat, double maxlon) {
    72                 Task task = new Task(action.dialog.newLayer.isSelected(), new BoundingBoxDownloader(minlat, minlon, maxlat, maxlon));
    73                 Main.worker.execute(task);
    74         }
    75 
    76         public JCheckBox getCheckBox() {
    77             return checkBox;
     63        @Override protected void cancel() {
     64            if (reader != null)
     65                reader.cancel();
     66        }
    7867    }
    7968
    80         public String getPreferencesSuffix() {
    81             return "gps";
     69    private JCheckBox checkBox = new JCheckBox(tr("Raw GPS data"));
     70
     71    public void download(DownloadAction action, double minlat, double minlon, double maxlat, double maxlon) {
     72        Task task = new Task(action.dialog.newLayer.isSelected(), new BoundingBoxDownloader(minlat, minlon, maxlat, maxlon));
     73        Main.worker.execute(task);
    8274    }
    8375
    84     public void loadUrl(boolean a,java.lang.String b) {
     76    public JCheckBox getCheckBox() {
     77        return checkBox;
     78    }
     79
     80    public String getPreferencesSuffix() {
     81        return "gps";
     82    }
     83
     84    public void loadUrl(boolean a,java.lang.String b) {
    8585        // FIXME this is not currently used
    8686    }
  • trunk/src/org/openstreetmap/josm/actions/downloadtasks/DownloadOsmTask.java

    r1146 r1169  
    3131    private static Bounds currentBounds;
    3232
    33         private static class Task extends PleaseWaitRunnable {
    34                 private OsmServerReader reader;
    35                 private DataSet dataSet;
    36                 private boolean newLayer;
     33    private static class Task extends PleaseWaitRunnable {
     34        private OsmServerReader reader;
     35        private DataSet dataSet;
     36        private boolean newLayer;
    3737
    38                 public Task(boolean newLayer, OsmServerReader reader) {
    39                         super(tr("Downloading data"));
    40                         this.reader = reader;
    41                         this.newLayer = newLayer;
    42                 }
     38        public Task(boolean newLayer, OsmServerReader reader) {
     39            super(tr("Downloading data"));
     40            this.reader = reader;
     41            this.newLayer = newLayer;
     42        }
    4343
    44                 @Override public void realRun() throws IOException, SAXException {
    45                         dataSet = reader.parseOsm();
    46                 }
     44        @Override public void realRun() throws IOException, SAXException {
     45            dataSet = reader.parseOsm();
     46        }
    4747
    48                 @Override protected void finish() {
    49                         if (dataSet == null)
    50                                 return; // user cancelled download or error occoured
    51                         if (dataSet.allPrimitives().isEmpty()) {
    52                                 errorMessage = tr("No data imported.");
     48        @Override protected void finish() {
     49            if (dataSet == null)
     50                return; // user cancelled download or error occoured
     51            if (dataSet.allPrimitives().isEmpty()) {
     52                errorMessage = tr("No data imported.");
    5353                // need to synthesize a download bounds lest the visual indication of downloaded
    5454                // area doesn't work
     
    5656            }
    5757
    58                         OsmDataLayer layer = new OsmDataLayer(dataSet, tr("Data Layer"), null);
    59                         if (newLayer)
    60                                 Main.main.addLayer(layer);
    61                         else
    62                                 Main.main.editLayer().mergeFrom(layer);
    63                 }
     58            OsmDataLayer layer = new OsmDataLayer(dataSet, tr("Data Layer"), null);
     59            if (newLayer)
     60                Main.main.addLayer(layer);
     61            else
     62                Main.main.editLayer().mergeFrom(layer);
     63        }
    6464
    65                 @Override protected void cancel() {
    66                         if (reader != null)
    67                                 reader.cancel();
    68                 }
    69         }
    70         private JCheckBox checkBox = new JCheckBox(tr("OpenStreetMap data"), true);
     65        @Override protected void cancel() {
     66            if (reader != null)
     67                reader.cancel();
     68        }
     69    }
     70    private JCheckBox checkBox = new JCheckBox(tr("OpenStreetMap data"), true);
    7171
    72         public void download(DownloadAction action, double minlat, double minlon, double maxlat, double maxlon) {
    73                 // Swap min and max if user has specified them the wrong way round
    74                 // (easy to do if you are crossing 0, for example)
    75                 // FIXME should perhaps be done in download dialog?
    76                 if (minlat > maxlat) {
    77                         double t = minlat; minlat = maxlat; maxlat = t;
    78                 }
    79                 if (minlon > maxlon) {
    80                         double t = minlon; minlon = maxlon; maxlon = t;
    81                 }
    82    
    83                 Task task = new Task(action != null && (action.dialog == null || action.dialog.newLayer.isSelected()), new BoundingBoxDownloader(minlat, minlon, maxlat, maxlon));
     72    public void download(DownloadAction action, double minlat, double minlon, double maxlat, double maxlon) {
     73        // Swap min and max if user has specified them the wrong way round
     74        // (easy to do if you are crossing 0, for example)
     75        // FIXME should perhaps be done in download dialog?
     76        if (minlat > maxlat) {
     77            double t = minlat; minlat = maxlat; maxlat = t;
     78        }
     79        if (minlon > maxlon) {
     80            double t = minlon; minlon = maxlon; maxlon = t;
     81        }
     82
     83        Task task = new Task(action != null && (action.dialog == null || action.dialog.newLayer.isSelected()), new BoundingBoxDownloader(minlat, minlon, maxlat, maxlon));
    8484        currentBounds = new Bounds(new LatLon(minlat, minlon), new LatLon(maxlat, maxlon));
    85                 Main.worker.execute(task);
     85        Main.worker.execute(task);
    8686    }
    87    
     87
    8888    public void loadUrl(boolean new_layer, String url) {
    8989        Task task = new Task(new_layer, new OsmServerLocationReader(url));
    9090        Main.worker.execute(task);
    9191    }
    92    
    93    
    94    
    9592
    96         public JCheckBox getCheckBox() {
    97             return checkBox;
     93
     94
     95
     96    public JCheckBox getCheckBox() {
     97        return checkBox;
    9898    }
    9999
    100         public String getPreferencesSuffix() {
    101             return "osm";
     100    public String getPreferencesSuffix() {
     101        return "osm";
    102102    }
    103103}
  • trunk/src/org/openstreetmap/josm/actions/mapmode/DeleteAction.java

    r1150 r1169  
    3636public class DeleteAction extends MapMode {
    3737
    38         /**
    39         * Construct a new DeleteAction. Mnemonic is the delete - key.
    40         * @param mapFrame The frame this action belongs to.
    41         */
    42         public DeleteAction(MapFrame mapFrame) {
    43                 super(tr("Delete Mode"),
    44                                 "delete",
    45                                 tr("Delete nodes or ways."),
    46                                 Shortcut.registerShortcut("mapmode:delete", tr("Mode: {0}",tr("Delete")), KeyEvent.VK_D, Shortcut.GROUP_EDIT),
    47                                 mapFrame,
    48                                 ImageProvider.getCursor("normal", "delete"));
    49         }
     38    /**
     39    * Construct a new DeleteAction. Mnemonic is the delete - key.
     40    * @param mapFrame The frame this action belongs to.
     41    */
     42    public DeleteAction(MapFrame mapFrame) {
     43        super(tr("Delete Mode"),
     44                "delete",
     45                tr("Delete nodes or ways."),
     46                Shortcut.registerShortcut("mapmode:delete", tr("Mode: {0}",tr("Delete")), KeyEvent.VK_D, Shortcut.GROUP_EDIT),
     47                mapFrame,
     48                ImageProvider.getCursor("normal", "delete"));
     49    }
    5050
    51         @Override public void enterMode() {
    52                 super.enterMode();
    53                 Main.map.mapView.addMouseListener(this);
    54         }
     51    @Override public void enterMode() {
     52        super.enterMode();
     53        Main.map.mapView.addMouseListener(this);
     54    }
    5555
    56         @Override public void exitMode() {
    57                 super.exitMode();
    58                 Main.map.mapView.removeMouseListener(this);
    59         }
     56    @Override public void exitMode() {
     57        super.exitMode();
     58        Main.map.mapView.removeMouseListener(this);
     59    }
    6060
    6161
    62         @Override public void actionPerformed(ActionEvent e) {
    63                 super.actionPerformed(e);
    64                 if(!Main.map.mapView.isDrawableLayer())
    65                         return;
    66                 doActionPerformed(e);
    67         }
     62    @Override public void actionPerformed(ActionEvent e) {
     63        super.actionPerformed(e);
     64        if(!Main.map.mapView.isDrawableLayer())
     65            return;
     66        doActionPerformed(e);
     67    }
    6868
    69         public void doActionPerformed(ActionEvent e) {
    70                 if(!Main.map.mapView.isDrawableLayer())
    71                         return;
    72                 boolean ctrl = (e.getModifiers() & ActionEvent.CTRL_MASK) != 0;
    73                 boolean alt = (e.getModifiers() & ActionEvent.ALT_MASK) != 0;
     69    public void doActionPerformed(ActionEvent e) {
     70        if(!Main.map.mapView.isDrawableLayer())
     71            return;
     72        boolean ctrl = (e.getModifiers() & ActionEvent.CTRL_MASK) != 0;
     73        boolean alt = (e.getModifiers() & ActionEvent.ALT_MASK) != 0;
    7474
    75                 Command c;
    76                 if (ctrl) {
    77                         c = DeleteCommand.deleteWithReferences(Main.ds.getSelected());
    78                 } else {
    79                         c = DeleteCommand.delete(Main.ds.getSelected(), !alt);
    80                 }
    81                 if (c != null) {
    82                         Main.main.undoRedo.add(c);
    83                 }
     75        Command c;
     76        if (ctrl) {
     77            c = DeleteCommand.deleteWithReferences(Main.ds.getSelected());
     78        } else {
     79            c = DeleteCommand.delete(Main.ds.getSelected(), !alt);
     80        }
     81        if (c != null) {
     82            Main.main.undoRedo.add(c);
     83        }
    8484
    85                 Main.ds.setSelected();
    86                 Main.map.repaint();
    87         }
     85        Main.ds.setSelected();
     86        Main.map.repaint();
     87    }
    8888
    89         /**
    90         * If user clicked with the left button, delete the nearest object.
    91         * position.
    92         */
    93         @Override public void mouseClicked(MouseEvent e) {
    94                 if (e.getButton() != MouseEvent.BUTTON1)
    95                         return;
    96                 if(!Main.map.mapView.isDrawableLayer())
    97                         return;
    98                 boolean ctrl = (e.getModifiers() & ActionEvent.CTRL_MASK) != 0;
    99                 boolean shift = (e.getModifiers() & ActionEvent.SHIFT_MASK) != 0;
    100                 boolean alt = (e.getModifiers() & ActionEvent.ALT_MASK) != 0;
     89    /**
     90    * If user clicked with the left button, delete the nearest object.
     91    * position.
     92    */
     93    @Override public void mouseClicked(MouseEvent e) {
     94        if (e.getButton() != MouseEvent.BUTTON1)
     95            return;
     96        if(!Main.map.mapView.isDrawableLayer())
     97            return;
     98        boolean ctrl = (e.getModifiers() & ActionEvent.CTRL_MASK) != 0;
     99        boolean shift = (e.getModifiers() & ActionEvent.SHIFT_MASK) != 0;
     100        boolean alt = (e.getModifiers() & ActionEvent.ALT_MASK) != 0;
    101101
    102                 OsmPrimitive sel = Main.map.mapView.getNearestNode(e.getPoint());
    103                 Command c = null;
    104                 if (sel == null) {
    105                         WaySegment ws = Main.map.mapView.getNearestWaySegment(e.getPoint());
    106                         if (ws != null) {
    107                                 if (shift) {
    108                                         c = DeleteCommand.deleteWaySegment(ws);
    109                                 } else if (ctrl) {
    110                                         c = DeleteCommand.deleteWithReferences(Collections.singleton((OsmPrimitive)ws.way));
    111                                 } else {
    112                                         c = DeleteCommand.delete(Collections.singleton((OsmPrimitive)ws.way), !alt);
    113                                 }
    114                         }
    115                 } else if (ctrl) {
    116                         c = DeleteCommand.deleteWithReferences(Collections.singleton(sel));
    117                 } else {
    118                         c = DeleteCommand.delete(Collections.singleton(sel), !alt);
    119                 }
    120                 if (c != null) {
    121                         Main.main.undoRedo.add(c);
    122                 }
     102        OsmPrimitive sel = Main.map.mapView.getNearestNode(e.getPoint());
     103        Command c = null;
     104        if (sel == null) {
     105            WaySegment ws = Main.map.mapView.getNearestWaySegment(e.getPoint());
     106            if (ws != null) {
     107                if (shift) {
     108                    c = DeleteCommand.deleteWaySegment(ws);
     109                } else if (ctrl) {
     110                    c = DeleteCommand.deleteWithReferences(Collections.singleton((OsmPrimitive)ws.way));
     111                } else {
     112                    c = DeleteCommand.delete(Collections.singleton((OsmPrimitive)ws.way), !alt);
     113                }
     114            }
     115        } else if (ctrl) {
     116            c = DeleteCommand.deleteWithReferences(Collections.singleton(sel));
     117        } else {
     118            c = DeleteCommand.delete(Collections.singleton(sel), !alt);
     119        }
     120        if (c != null) {
     121            Main.main.undoRedo.add(c);
     122        }
    123123
    124                 Main.ds.setSelected();
    125                 Main.map.mapView.repaint();
    126         }
     124        Main.ds.setSelected();
     125        Main.map.mapView.repaint();
     126    }
    127127
    128         @Override public String getModeHelpText() {
    129                 return tr("Click to delete. Shift: delete way segment. Alt: don't delete unused nodes when deleting a way. Ctrl: delete referring objects.");
    130         }
     128    @Override public String getModeHelpText() {
     129        return tr("Click to delete. Shift: delete way segment. Alt: don't delete unused nodes when deleting a way. Ctrl: delete referring objects.");
     130    }
    131131}
  • trunk/src/org/openstreetmap/josm/actions/mapmode/DrawAction.java

    r1084 r1169  
    5858public class DrawAction extends MapMode implements MapViewPaintable, SelectionChangedListener, AWTEventListener {
    5959
    60         private static Node lastUsedNode = null;
    61         private double PHI=Math.toRadians(90);
    62 
    63         private boolean ctrl;
    64         private boolean alt;
    65         private boolean shift;
    66         private boolean mouseOnExistingNode;
    67         private boolean drawHelperLine;
    68         private Point mousePos;
    69         private Color selectedColor;
    70 
    71         private Node currentBaseNode;
    72         private EastNorth currentMouseEastNorth;
    73 
    74         public DrawAction(MapFrame mapFrame) {
    75                 super(tr("Draw"), "node/autonode", tr("Draw nodes"),
    76                                 Shortcut.registerShortcut("mapmode:draw", tr("Mode: {0}", tr("Draw")), KeyEvent.VK_A, Shortcut.GROUP_EDIT),
    77                                 mapFrame, getCursor());
    78 
    79                 // Add extra shortcut N
    80                 Main.contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
    81                         Shortcut.registerShortcut("mapmode:drawfocus", tr("Mode: Draw Focus"), KeyEvent.VK_N, Shortcut.GROUP_EDIT).getKeyStroke(), tr("Draw"));
    82 
    83                 //putValue("help", "Action/AddNode/Autnode");
    84                 selectedColor = Main.pref.getColor(marktr("selected"), Color.YELLOW);
    85 
    86                 drawHelperLine = Main.pref.getBoolean("draw.helper-line", true);
    87         }
    88 
    89         private static Cursor getCursor() {
    90                 try {
    91                 return ImageProvider.getCursor("crosshair", null);
     60    private static Node lastUsedNode = null;
     61    private double PHI=Math.toRadians(90);
     62
     63    private boolean ctrl;
     64    private boolean alt;
     65    private boolean shift;
     66    private boolean mouseOnExistingNode;
     67    private boolean drawHelperLine;
     68    private Point mousePos;
     69    private Color selectedColor;
     70
     71    private Node currentBaseNode;
     72    private EastNorth currentMouseEastNorth;
     73
     74    public DrawAction(MapFrame mapFrame) {
     75        super(tr("Draw"), "node/autonode", tr("Draw nodes"),
     76                Shortcut.registerShortcut("mapmode:draw", tr("Mode: {0}", tr("Draw")), KeyEvent.VK_A, Shortcut.GROUP_EDIT),
     77                mapFrame, getCursor());
     78
     79        // Add extra shortcut N
     80        Main.contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
     81            Shortcut.registerShortcut("mapmode:drawfocus", tr("Mode: Draw Focus"), KeyEvent.VK_N, Shortcut.GROUP_EDIT).getKeyStroke(), tr("Draw"));
     82
     83        //putValue("help", "Action/AddNode/Autnode");
     84        selectedColor = Main.pref.getColor(marktr("selected"), Color.YELLOW);
     85
     86        drawHelperLine = Main.pref.getBoolean("draw.helper-line", true);
     87    }
     88
     89    private static Cursor getCursor() {
     90        try {
     91            return ImageProvider.getCursor("crosshair", null);
    9292        } catch (Exception e) {
    9393        }
    94             return Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR);
    95     }
    96 
    97         @Override public void enterMode() {
    98                 super.enterMode();
    99                 Main.map.mapView.addMouseListener(this);
    100                 Main.map.mapView.addMouseMotionListener(this);
    101                 Main.map.mapView.addTemporaryLayer(this);
    102                 DataSet.selListeners.add(this);
    103                 try {
    104                         Toolkit.getDefaultToolkit().addAWTEventListener(this, AWTEvent.KEY_EVENT_MASK);
    105                 } catch (SecurityException ex) {
    106                 }
    107                 // would like to but haven't got mouse position yet:
    108                 // computeHelperLine(false, false, false);
    109         }
    110         @Override public void exitMode() {
    111                 super.exitMode();
    112                 Main.map.mapView.removeMouseListener(this);
    113                 Main.map.mapView.removeMouseMotionListener(this);
    114                 Main.map.mapView.removeTemporaryLayer(this);
    115                 DataSet.selListeners.remove(this);
    116                 try {
    117                         Toolkit.getDefaultToolkit().removeAWTEventListener(this);
    118                 } catch (SecurityException ex) {
    119                 }
    120         }
    121 
    122         /**
    123         * redraw to (possibly) get rid of helper line if selection changes.
    124         */
    125         public void eventDispatched(AWTEvent event) {
    126                 if(!Main.map.mapView.isDrawableLayer())
    127                         return;
    128                 InputEvent e = (InputEvent) event;
    129                 ctrl = (e.getModifiers() & ActionEvent.CTRL_MASK) != 0;
    130                 alt = (e.getModifiers() & ActionEvent.ALT_MASK) != 0;
    131                 shift = (e.getModifiers() & ActionEvent.SHIFT_MASK) != 0;
    132                 computeHelperLine();
    133         }
    134         /**
    135         * redraw to (possibly) get rid of helper line if selection changes.
    136         */
    137         public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) {
    138                 if(!Main.map.mapView.isDrawableLayer())
    139                         return;
    140                 computeHelperLine();
    141         }
    142 
    143         /**
    144         * If user clicked with the left button, add a node at the current mouse
    145         * position.
    146         *
    147         * If in nodeway mode, insert the node into the way.
    148         */
    149         @Override public void mouseClicked(MouseEvent e) {
    150 
    151                 if (e.getButton() != MouseEvent.BUTTON1)
    152                         return;
    153                 if(!Main.map.mapView.isDrawableLayer())
    154                         return;
    155 
    156                 // we copy ctrl/alt/shift from the event just in case our global
    157                 // AWTEvent didn't make it through the security manager. Unclear
    158                 // if that can ever happen but better be safe.
    159                 ctrl = (e.getModifiers() & ActionEvent.CTRL_MASK) != 0;
    160                 alt = (e.getModifiers() & ActionEvent.ALT_MASK) != 0;
    161                 shift = (e.getModifiers() & ActionEvent.SHIFT_MASK) != 0;
    162                 mousePos = e.getPoint();
    163 
    164                 Collection<OsmPrimitive> selection = Main.ds.getSelected();
    165                 Collection<Command> cmds = new LinkedList<Command>();
    166 
    167                 ArrayList<Way> reuseWays = new ArrayList<Way>(),
    168                         replacedWays = new ArrayList<Way>();
    169                 boolean newNode = false;
    170                 Node n = null;
    171 
    172                 if (!ctrl) {
    173                         n = Main.map.mapView.getNearestNode(mousePos);
    174                 }
    175 
    176                 if (n != null) {
    177                         // user clicked on node
    178                         if (shift || selection.isEmpty()) {
    179                                 // select the clicked node and do nothing else
    180                                 // (this is just a convenience option so that people don't
    181                                 // have to switch modes)
    182                                 Main.ds.setSelected(n);
    183                                 return;
    184                         }
    185 
    186                 } else {
    187                         // no node found in clicked area
    188                         n = new Node(Main.map.mapView.getLatLon(e.getX(), e.getY()));
    189                         if (n.coor.isOutSideWorld()) {
    190                                 JOptionPane.showMessageDialog(Main.parent,
    191                                         tr("Cannot add a node outside of the world."));
    192                                 return;
    193                         }
    194                         newNode = true;
    195 
    196                         cmds.add(new AddCommand(n));
    197 
    198                         if (!ctrl) {
    199                                 // Insert the node into all the nearby way segments
    200                                 List<WaySegment> wss = Main.map.mapView.getNearestWaySegments(e.getPoint());
    201                                 Map<Way, List<Integer>> insertPoints = new HashMap<Way, List<Integer>>();
    202                                 for (WaySegment ws : wss) {
    203                                         List<Integer> is;
    204                                         if (insertPoints.containsKey(ws.way)) {
    205                                                 is = insertPoints.get(ws.way);
    206                                         } else {
    207                                                 is = new ArrayList<Integer>();
    208                                                 insertPoints.put(ws.way, is);
    209                                         }
    210 
    211                                         is.add(ws.lowerIndex);
    212                                 }
    213 
    214                                 Set<Pair<Node,Node>> segSet = new HashSet<Pair<Node,Node>>();
    215 
    216                                 for (Map.Entry<Way, List<Integer>> insertPoint : insertPoints.entrySet()) {
    217                                         Way w = insertPoint.getKey();
    218                                         List<Integer> is = insertPoint.getValue();
    219 
    220                                         Way wnew = new Way(w);
    221 
    222                                         pruneSuccsAndReverse(is);
    223                                         for (int i : is) segSet.add(
    224                                                 Pair.sort(new Pair<Node,Node>(w.nodes.get(i), w.nodes.get(i+1))));
    225                                         for (int i : is) wnew.nodes.add(i + 1, n);
    226 
    227                                         cmds.add(new ChangeCommand(insertPoint.getKey(), wnew));
    228                                         replacedWays.add(insertPoint.getKey());
    229                                         reuseWays.add(wnew);
    230                                 }
    231 
    232                                 adjustNode(segSet, n);
    233                         }
    234                 }
    235 
    236                 // This part decides whether or not a "segment" (i.e. a connection) is made to an
    237                 // existing node.
    238 
    239                 // For a connection to be made, the user must either have a node selected (connection
    240                 // is made to that node), or he must have a way selected *and* one of the endpoints
    241                 // of that way must be the last used node (connection is made to last used node), or
    242                 // he must have a way and a node selected (connection is made to the selected node).
    243 
    244                 boolean extendedWay = false;
    245 
    246                 if (!shift && selection.size() > 0 && selection.size() < 3) {
    247 
    248                         Node selectedNode = null;
    249                         Way selectedWay = null;
    250 
    251                         for (OsmPrimitive p : selection) {
    252                                 if (p instanceof Node) {
    253                                         if (selectedNode != null) return;
    254                                         selectedNode = (Node) p;
    255                                 } else if (p instanceof Way) {
    256                                         if (selectedWay != null) return;
    257                                         selectedWay = (Way) p;
    258                                 }
    259                         }
    260 
    261                         // the node from which we make a connection
    262                         Node n0 = null;
    263 
    264                         if (selectedNode == null) {
    265                                 if (selectedWay == null) return;
    266                                 if (lastUsedNode == selectedWay.nodes.get(0) || lastUsedNode == selectedWay.nodes.get(selectedWay.nodes.size()-1)) {
    267                                         n0 = lastUsedNode;
    268                                 }
    269                         } else if (selectedWay == null) {
    270                                 n0 = selectedNode;
    271                         } else {
    272                                 if (selectedNode == selectedWay.nodes.get(0) || selectedNode == selectedWay.nodes.get(selectedWay.nodes.size()-1)) {
    273                                         n0 = selectedNode;
    274                                 }
    275                         }
    276 
    277                         if (n0 == null || n0 == n) {
    278                                 return; // Don't create zero length way segments.
    279                         }
    280 
    281                         // Ok we know now that we'll insert a line segment, but will it connect to an
    282                         // existing way or make a new way of its own? The "alt" modifier means that the
    283                         // user wants a new way.
    284 
    285                         Way way = alt ? null : (selectedWay != null) ? selectedWay : getWayForNode(n0);
    286                         if (way == null) {
    287                                 way = new Way();
    288                                 way.nodes.add(n0);
    289                                 cmds.add(new AddCommand(way));
    290                         } else {
    291                                 int i;
    292                                 if ((i = replacedWays.indexOf(way)) != -1) {
    293                                         way = reuseWays.get(i);
    294                                 } else {
    295                                         Way wnew = new Way(way);
    296                                         cmds.add(new ChangeCommand(way, wnew));
    297                                         way = wnew;
    298                                 }
    299                         }
    300 
    301                         if (way.nodes.get(way.nodes.size() - 1) == n0) {
    302                                 way.nodes.add(n);
    303                         } else {
    304                                 way.nodes.add(0, n);
    305                         }
    306 
    307                         extendedWay = true;
    308                         Main.ds.setSelected(way);
    309                 }
    310 
    311                 String title;
    312                 if (!extendedWay && !newNode) {
    313                         return; // We didn't do anything.
    314                 } else if (!extendedWay) {
    315                         if (reuseWays.isEmpty()) {
    316                                 title = tr("Add node");
    317                         } else {
    318                                 title = tr("Add node into way");
    319                         }
    320                         for (Way w : reuseWays) w.selected = false;
    321                         Main.ds.setSelected(n);
    322                 } else if (!newNode) {
    323                         title = tr("Connect existing way to node");
    324                 } else if (reuseWays.isEmpty()) {
    325                         title = tr("Add a new node to an existing way");
    326                 } else {
    327                         title = tr("Add node into way and connect");
    328                 }
    329 
    330                 Command c = new SequenceCommand(title, cmds);
    331 
    332                 Main.main.undoRedo.add(c);
    333                 lastUsedNode = n;
    334                 computeHelperLine();
    335                 Main.map.mapView.repaint();
    336         }
    337 
    338         @Override public void mouseMoved(MouseEvent e) {
    339                 if(!Main.map.mapView.isDrawableLayer())
    340                         return;
    341 
    342                 // we copy ctrl/alt/shift from the event just in case our global
    343                 // AWTEvent didn't make it through the security manager. Unclear
    344                 // if that can ever happen but better be safe.
    345 
    346                 ctrl = (e.getModifiers() & ActionEvent.CTRL_MASK) != 0;
    347                 alt = (e.getModifiers() & ActionEvent.ALT_MASK) != 0;
    348                 shift = (e.getModifiers() & ActionEvent.SHIFT_MASK) != 0;
    349                 mousePos = e.getPoint();
    350 
    351                 computeHelperLine();
    352         }
    353 
    354         /**
    355         * This method prepares data required for painting the "helper line" from
    356         * the last used position to the mouse cursor. It duplicates some code from
    357         * mouseClicked() (FIXME).
    358         */
    359         private void computeHelperLine() {
    360                 if (mousePos == null) {
    361                         // Don't draw the line.
    362                         currentMouseEastNorth = null;
    363                         currentBaseNode = null;
    364                         return;
    365                 }
    366 
    367                 double distance = -1;
    368                 double angle = -1;
    369 
    370                 Collection<OsmPrimitive> selection = Main.ds.getSelected();
    371 
    372                 Node selectedNode = null;
    373                 Way selectedWay = null;
    374                 Node currentMouseNode = null;
    375                 mouseOnExistingNode = false;
    376 
    377                 Main.map.statusLine.setAngle(-1);
    378                 Main.map.statusLine.setHeading(-1);
    379                 Main.map.statusLine.setDist(-1);
    380 
    381                 if (!ctrl && mousePos != null) {
    382                         currentMouseNode = Main.map.mapView.getNearestNode(mousePos);
    383                 }
    384 
    385                 if (currentMouseNode != null) {
    386                         // user clicked on node
    387                         if (selection.isEmpty()) return;
    388                         currentMouseEastNorth = currentMouseNode.eastNorth;
    389                         mouseOnExistingNode = true;
    390                 } else {
    391                         // no node found in clicked area
    392                         currentMouseEastNorth = Main.map.mapView.getEastNorth(mousePos.x, mousePos.y);
    393                 }
    394 
    395                 for (OsmPrimitive p : selection) {
    396                         if (p instanceof Node) {
    397                                 if (selectedNode != null) return;
    398                                 selectedNode = (Node) p;
    399                         } else if (p instanceof Way) {
    400                                 if (selectedWay != null) return;
    401                                 selectedWay = (Way) p;
    402                         }
    403                 }
    404 
    405                 // the node from which we make a connection
    406                 currentBaseNode = null;
    407                 Node previousNode = null;
    408 
    409                 if (selectedNode == null) {
    410                         if (selectedWay == null) return;
    411                         if (lastUsedNode == selectedWay.nodes.get(0) || lastUsedNode == selectedWay.nodes.get(selectedWay.nodes.size()-1)) {
    412                                 currentBaseNode = lastUsedNode;
    413                                 if (lastUsedNode == selectedWay.nodes.get(selectedWay.nodes.size()-1) && selectedWay.nodes.size() > 1) {
    414                                         previousNode = selectedWay.nodes.get(selectedWay.nodes.size()-2);
    415                                 }
    416                         }
    417                 } else if (selectedWay == null) {
    418                         currentBaseNode = selectedNode;
    419                 } else {
    420                         if (selectedNode == selectedWay.nodes.get(0) || selectedNode == selectedWay.nodes.get(selectedWay.nodes.size()-1)) {
    421                                 currentBaseNode = selectedNode;
    422                         }
    423                 }
    424 
    425                 if (currentBaseNode == null || currentBaseNode == currentMouseNode) {
    426                         return; // Don't create zero length way segments.
    427                 }
    428 
    429                 // find out the distance, in metres, between the base point and the mouse cursor
    430                 LatLon mouseLatLon = Main.proj.eastNorth2latlon(currentMouseEastNorth);
    431                 distance = currentBaseNode.coor.greatCircleDistance(mouseLatLon);
    432                 double hdg = Math.toDegrees(currentBaseNode.coor.heading(mouseLatLon));
    433                 if (previousNode != null) {
    434                         angle = hdg - Math.toDegrees(previousNode.coor.heading(currentBaseNode.coor));
    435                         if (angle < 0) angle += 360;
    436                 }
    437                 Main.map.statusLine.setAngle(angle);
    438                 Main.map.statusLine.setHeading(hdg);
    439                 Main.map.statusLine.setDist(distance);
    440                 updateStatusLine();
    441 
    442                 if (!drawHelperLine) return;
    443 
    444                 Main.map.mapView.repaint();
    445         }
    446 
    447         /**
    448         * Repaint on mouse exit so that the helper line goes away.
    449         */
    450         @Override public void mouseExited(MouseEvent e) {
    451                 if(!Main.map.mapView.isDrawableLayer())
    452                         return;
    453                 mousePos = e.getPoint();
    454                 Main.map.mapView.repaint();
    455         }
    456 
    457         /**
    458         * @return If the node is the end of exactly one way, return this.
    459          *      <code>null</code> otherwise.
    460         */
    461         public static Way getWayForNode(Node n) {
    462                 Way way = null;
    463                 for (Way w : Main.ds.ways) {
    464                         if (w.deleted || w.incomplete || w.nodes.size() < 1) continue;
    465                         Node firstNode = w.nodes.get(0);
    466                         Node lastNode = w.nodes.get(w.nodes.size() - 1);
    467                         if ((firstNode == n || lastNode == n) && (firstNode != lastNode)) {
    468                                 if (way != null)
    469                                         return null;
    470                                 way = w;
    471                         }
    472                 }
    473                 return way;
    474         }
    475 
    476         private static void pruneSuccsAndReverse(List<Integer> is) {
    477                 //if (is.size() < 2) return;
    478 
    479                 HashSet<Integer> is2 = new HashSet<Integer>();
    480                 for (int i : is) {
    481                         if (!is2.contains(i - 1) && !is2.contains(i + 1)) {
    482                                 is2.add(i);
    483                         }
    484                 }
    485                 is.clear();
    486                 is.addAll(is2);
    487                 Collections.sort(is);
    488                 Collections.reverse(is);
    489         }
    490 
    491         /**
    492         * Adjusts the position of a node to lie on a segment (or a segment
    493         * intersection).
    494         *
    495         * If one or more than two segments are passed, the node is adjusted
    496         * to lie on the first segment that is passed.
    497         *
    498         * If two segments are passed, the node is adjusted to be at their
    499         * intersection.
    500         *
    501         * No action is taken if no segments are passed.
    502         *
    503         * @param segs the segments to use as a reference when adjusting
    504         * @param n the node to adjust
    505         */
    506         private static void adjustNode(Collection<Pair<Node,Node>> segs, Node n) {
    507 
    508                 switch (segs.size()) {
    509                 case 0:
    510                         return;
    511                 case 2:
    512                         // This computes the intersection between
    513                         // the two segments and adjusts the node position.
    514                         Iterator<Pair<Node,Node>> i = segs.iterator();
    515                         Pair<Node,Node> seg = i.next();
    516                         EastNorth A = seg.a.eastNorth;
    517                         EastNorth B = seg.b.eastNorth;
    518                         seg = i.next();
    519                         EastNorth C = seg.a.eastNorth;
    520                         EastNorth D = seg.b.eastNorth;
    521 
    522                         double u=det(B.east() - A.east(), B.north() - A.north(), C.east() - D.east(), C.north() - D.north());
    523                        
    524                         // Check for parallel segments and do nothing if they are
    525                         // In practice this will probably only happen when a way has been duplicated
    526                        
    527                         if (u == 0) return;
    528                        
    529                         // q is a number between 0 and 1
    530                         // It is the point in the segment where the intersection occurs
    531                         // if the segment is scaled to lenght 1
    532                        
    533                         double q = det(B.north() - C.north(), B.east() - C.east(), D.north() - C.north(), D.east() - C.east()) / u;
    534                         EastNorth intersection = new EastNorth(
    535                                         B.east() + q * (A.east() - B.east()),
    536                                         B.north() + q * (A.north() - B.north()));
    537 
    538                         int snapToIntersectionThreshold=0;
    539                         try { snapToIntersectionThreshold = Integer.parseInt(Main.pref.get("edit.snap-intersection-threshold","10")); } catch (NumberFormatException x) {}
    540 
    541                         // only adjust to intersection if within snapToIntersectionThreshold pixel of mouse click; otherwise
    542                         // fall through to default action.
    543                         // (for semi-parallel lines, intersection might be miles away!)
    544                         if (Main.map.mapView.getPoint(n.eastNorth).distance(Main.map.mapView.getPoint(intersection)) < snapToIntersectionThreshold) {
    545                                 n.eastNorth = intersection;
    546                                 return;
    547                         }
    548 
    549                 default:
    550                         EastNorth P = n.eastNorth;
    551                         seg = segs.iterator().next();
    552                         A = seg.a.eastNorth;
    553                         B = seg.b.eastNorth;
    554                         double a = P.distanceSq(B);
    555                         double b = P.distanceSq(A);
    556                         double c = A.distanceSq(B);
     94        return Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR);
     95    }
     96
     97    @Override public void enterMode() {
     98        super.enterMode();
     99        Main.map.mapView.addMouseListener(this);
     100        Main.map.mapView.addMouseMotionListener(this);
     101        Main.map.mapView.addTemporaryLayer(this);
     102        DataSet.selListeners.add(this);
     103        try {
     104            Toolkit.getDefaultToolkit().addAWTEventListener(this, AWTEvent.KEY_EVENT_MASK);
     105        } catch (SecurityException ex) {
     106        }
     107        // would like to but haven't got mouse position yet:
     108        // computeHelperLine(false, false, false);
     109    }
     110    @Override public void exitMode() {
     111        super.exitMode();
     112        Main.map.mapView.removeMouseListener(this);
     113        Main.map.mapView.removeMouseMotionListener(this);
     114        Main.map.mapView.removeTemporaryLayer(this);
     115        DataSet.selListeners.remove(this);
     116        try {
     117            Toolkit.getDefaultToolkit().removeAWTEventListener(this);
     118        } catch (SecurityException ex) {
     119        }
     120    }
     121
     122    /**
     123    * redraw to (possibly) get rid of helper line if selection changes.
     124    */
     125    public void eventDispatched(AWTEvent event) {
     126        if(!Main.map.mapView.isDrawableLayer())
     127            return;
     128        InputEvent e = (InputEvent) event;
     129        ctrl = (e.getModifiers() & ActionEvent.CTRL_MASK) != 0;
     130        alt = (e.getModifiers() & ActionEvent.ALT_MASK) != 0;
     131        shift = (e.getModifiers() & ActionEvent.SHIFT_MASK) != 0;
     132        computeHelperLine();
     133    }
     134    /**
     135    * redraw to (possibly) get rid of helper line if selection changes.
     136    */
     137    public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) {
     138        if(!Main.map.mapView.isDrawableLayer())
     139            return;
     140        computeHelperLine();
     141    }
     142
     143    /**
     144    * If user clicked with the left button, add a node at the current mouse
     145    * position.
     146    *
     147    * If in nodeway mode, insert the node into the way.
     148    */
     149    @Override public void mouseClicked(MouseEvent e) {
     150
     151        if (e.getButton() != MouseEvent.BUTTON1)
     152            return;
     153        if(!Main.map.mapView.isDrawableLayer())
     154            return;
     155
     156        // we copy ctrl/alt/shift from the event just in case our global
     157        // AWTEvent didn't make it through the security manager. Unclear
     158        // if that can ever happen but better be safe.
     159        ctrl = (e.getModifiers() & ActionEvent.CTRL_MASK) != 0;
     160        alt = (e.getModifiers() & ActionEvent.ALT_MASK) != 0;
     161        shift = (e.getModifiers() & ActionEvent.SHIFT_MASK) != 0;
     162        mousePos = e.getPoint();
     163
     164        Collection<OsmPrimitive> selection = Main.ds.getSelected();
     165        Collection<Command> cmds = new LinkedList<Command>();
     166
     167        ArrayList<Way> reuseWays = new ArrayList<Way>(),
     168            replacedWays = new ArrayList<Way>();
     169        boolean newNode = false;
     170        Node n = null;
     171
     172        if (!ctrl) {
     173            n = Main.map.mapView.getNearestNode(mousePos);
     174        }
     175
     176        if (n != null) {
     177            // user clicked on node
     178            if (shift || selection.isEmpty()) {
     179                // select the clicked node and do nothing else
     180                // (this is just a convenience option so that people don't
     181                // have to switch modes)
     182                Main.ds.setSelected(n);
     183                return;
     184            }
     185
     186        } else {
     187            // no node found in clicked area
     188            n = new Node(Main.map.mapView.getLatLon(e.getX(), e.getY()));
     189            if (n.coor.isOutSideWorld()) {
     190                JOptionPane.showMessageDialog(Main.parent,
     191                    tr("Cannot add a node outside of the world."));
     192                return;
     193            }
     194            newNode = true;
     195
     196            cmds.add(new AddCommand(n));
     197
     198            if (!ctrl) {
     199                // Insert the node into all the nearby way segments
     200                List<WaySegment> wss = Main.map.mapView.getNearestWaySegments(e.getPoint());
     201                Map<Way, List<Integer>> insertPoints = new HashMap<Way, List<Integer>>();
     202                for (WaySegment ws : wss) {
     203                    List<Integer> is;
     204                    if (insertPoints.containsKey(ws.way)) {
     205                        is = insertPoints.get(ws.way);
     206                    } else {
     207                        is = new ArrayList<Integer>();
     208                        insertPoints.put(ws.way, is);
     209                    }
     210
     211                    is.add(ws.lowerIndex);
     212                }
     213
     214                Set<Pair<Node,Node>> segSet = new HashSet<Pair<Node,Node>>();
     215
     216                for (Map.Entry<Way, List<Integer>> insertPoint : insertPoints.entrySet()) {
     217                    Way w = insertPoint.getKey();
     218                    List<Integer> is = insertPoint.getValue();
     219
     220                    Way wnew = new Way(w);
     221
     222                    pruneSuccsAndReverse(is);
     223                    for (int i : is) segSet.add(
     224                        Pair.sort(new Pair<Node,Node>(w.nodes.get(i), w.nodes.get(i+1))));
     225                    for (int i : is) wnew.nodes.add(i + 1, n);
     226
     227                    cmds.add(new ChangeCommand(insertPoint.getKey(), wnew));
     228                    replacedWays.add(insertPoint.getKey());
     229                    reuseWays.add(wnew);
     230                }
     231
     232                adjustNode(segSet, n);
     233            }
     234        }
     235
     236        // This part decides whether or not a "segment" (i.e. a connection) is made to an
     237        // existing node.
     238
     239        // For a connection to be made, the user must either have a node selected (connection
     240        // is made to that node), or he must have a way selected *and* one of the endpoints
     241        // of that way must be the last used node (connection is made to last used node), or
     242        // he must have a way and a node selected (connection is made to the selected node).
     243
     244        boolean extendedWay = false;
     245
     246        if (!shift && selection.size() > 0 && selection.size() < 3) {
     247
     248            Node selectedNode = null;
     249            Way selectedWay = null;
     250
     251            for (OsmPrimitive p : selection) {
     252                if (p instanceof Node) {
     253                    if (selectedNode != null) return;
     254                    selectedNode = (Node) p;
     255                } else if (p instanceof Way) {
     256                    if (selectedWay != null) return;
     257                    selectedWay = (Way) p;
     258                }
     259            }
     260
     261            // the node from which we make a connection
     262            Node n0 = null;
     263
     264            if (selectedNode == null) {
     265                if (selectedWay == null) return;
     266                if (lastUsedNode == selectedWay.nodes.get(0) || lastUsedNode == selectedWay.nodes.get(selectedWay.nodes.size()-1)) {
     267                    n0 = lastUsedNode;
     268                }
     269            } else if (selectedWay == null) {
     270                n0 = selectedNode;
     271            } else {
     272                if (selectedNode == selectedWay.nodes.get(0) || selectedNode == selectedWay.nodes.get(selectedWay.nodes.size()-1)) {
     273                    n0 = selectedNode;
     274                }
     275            }
     276
     277            if (n0 == null || n0 == n) {
     278                return; // Don't create zero length way segments.
     279            }
     280
     281            // Ok we know now that we'll insert a line segment, but will it connect to an
     282            // existing way or make a new way of its own? The "alt" modifier means that the
     283            // user wants a new way.
     284
     285            Way way = alt ? null : (selectedWay != null) ? selectedWay : getWayForNode(n0);
     286            if (way == null) {
     287                way = new Way();
     288                way.nodes.add(n0);
     289                cmds.add(new AddCommand(way));
     290            } else {
     291                int i;
     292                if ((i = replacedWays.indexOf(way)) != -1) {
     293                    way = reuseWays.get(i);
     294                } else {
     295                    Way wnew = new Way(way);
     296                    cmds.add(new ChangeCommand(way, wnew));
     297                    way = wnew;
     298                }
     299            }
     300
     301            if (way.nodes.get(way.nodes.size() - 1) == n0) {
     302                way.nodes.add(n);
     303            } else {
     304                way.nodes.add(0, n);
     305            }
     306
     307            extendedWay = true;
     308            Main.ds.setSelected(way);
     309        }
     310
     311        String title;
     312        if (!extendedWay && !newNode) {
     313            return; // We didn't do anything.
     314        } else if (!extendedWay) {
     315            if (reuseWays.isEmpty()) {
     316                title = tr("Add node");
     317            } else {
     318                title = tr("Add node into way");
     319            }
     320            for (Way w : reuseWays) w.selected = false;
     321            Main.ds.setSelected(n);
     322        } else if (!newNode) {
     323            title = tr("Connect existing way to node");
     324        } else if (reuseWays.isEmpty()) {
     325            title = tr("Add a new node to an existing way");
     326        } else {
     327            title = tr("Add node into way and connect");
     328        }
     329
     330        Command c = new SequenceCommand(title, cmds);
     331
     332        Main.main.undoRedo.add(c);
     333        lastUsedNode = n;
     334        computeHelperLine();
     335        Main.map.mapView.repaint();
     336    }
     337
     338    @Override public void mouseMoved(MouseEvent e) {
     339        if(!Main.map.mapView.isDrawableLayer())
     340            return;
     341
     342        // we copy ctrl/alt/shift from the event just in case our global
     343        // AWTEvent didn't make it through the security manager. Unclear
     344        // if that can ever happen but better be safe.
     345
     346        ctrl = (e.getModifiers() & ActionEvent.CTRL_MASK) != 0;
     347        alt = (e.getModifiers() & ActionEvent.ALT_MASK) != 0;
     348        shift = (e.getModifiers() & ActionEvent.SHIFT_MASK) != 0;
     349        mousePos = e.getPoint();
     350
     351        computeHelperLine();
     352    }
     353
     354    /**
     355    * This method prepares data required for painting the "helper line" from
     356    * the last used position to the mouse cursor. It duplicates some code from
     357    * mouseClicked() (FIXME).
     358    */
     359    private void computeHelperLine() {
     360        if (mousePos == null) {
     361            // Don't draw the line.
     362            currentMouseEastNorth = null;
     363            currentBaseNode = null;
     364            return;
     365        }
     366
     367        double distance = -1;
     368        double angle = -1;
     369
     370        Collection<OsmPrimitive> selection = Main.ds.getSelected();
     371
     372        Node selectedNode = null;
     373        Way selectedWay = null;
     374        Node currentMouseNode = null;
     375        mouseOnExistingNode = false;
     376
     377        Main.map.statusLine.setAngle(-1);
     378        Main.map.statusLine.setHeading(-1);
     379        Main.map.statusLine.setDist(-1);
     380
     381        if (!ctrl && mousePos != null) {
     382            currentMouseNode = Main.map.mapView.getNearestNode(mousePos);
     383        }
     384
     385        if (currentMouseNode != null) {
     386            // user clicked on node
     387            if (selection.isEmpty()) return;
     388            currentMouseEastNorth = currentMouseNode.eastNorth;
     389            mouseOnExistingNode = true;
     390        } else {
     391            // no node found in clicked area
     392            currentMouseEastNorth = Main.map.mapView.getEastNorth(mousePos.x, mousePos.y);
     393        }
     394
     395        for (OsmPrimitive p : selection) {
     396            if (p instanceof Node) {
     397                if (selectedNode != null) return;
     398                selectedNode = (Node) p;
     399            } else if (p instanceof Way) {
     400                if (selectedWay != null) return;
     401                selectedWay = (Way) p;
     402            }
     403        }
     404
     405        // the node from which we make a connection
     406        currentBaseNode = null;
     407        Node previousNode = null;
     408
     409        if (selectedNode == null) {
     410            if (selectedWay == null) return;
     411            if (lastUsedNode == selectedWay.nodes.get(0) || lastUsedNode == selectedWay.nodes.get(selectedWay.nodes.size()-1)) {
     412                currentBaseNode = lastUsedNode;
     413                if (lastUsedNode == selectedWay.nodes.get(selectedWay.nodes.size()-1) && selectedWay.nodes.size() > 1) {
     414                    previousNode = selectedWay.nodes.get(selectedWay.nodes.size()-2);
     415                }
     416            }
     417        } else if (selectedWay == null) {
     418            currentBaseNode = selectedNode;
     419        } else {
     420            if (selectedNode == selectedWay.nodes.get(0) || selectedNode == selectedWay.nodes.get(selectedWay.nodes.size()-1)) {
     421                currentBaseNode = selectedNode;
     422            }
     423        }
     424
     425        if (currentBaseNode == null || currentBaseNode == currentMouseNode) {
     426            return; // Don't create zero length way segments.
     427        }
     428
     429        // find out the distance, in metres, between the base point and the mouse cursor
     430        LatLon mouseLatLon = Main.proj.eastNorth2latlon(currentMouseEastNorth);
     431        distance = currentBaseNode.coor.greatCircleDistance(mouseLatLon);
     432        double hdg = Math.toDegrees(currentBaseNode.coor.heading(mouseLatLon));
     433        if (previousNode != null) {
     434            angle = hdg - Math.toDegrees(previousNode.coor.heading(currentBaseNode.coor));
     435            if (angle < 0) angle += 360;
     436        }
     437        Main.map.statusLine.setAngle(angle);
     438        Main.map.statusLine.setHeading(hdg);
     439        Main.map.statusLine.setDist(distance);
     440        updateStatusLine();
     441
     442        if (!drawHelperLine) return;
     443
     444        Main.map.mapView.repaint();
     445    }
     446
     447    /**
     448    * Repaint on mouse exit so that the helper line goes away.
     449    */
     450    @Override public void mouseExited(MouseEvent e) {
     451        if(!Main.map.mapView.isDrawableLayer())
     452            return;
     453        mousePos = e.getPoint();
     454        Main.map.mapView.repaint();
     455    }
     456
     457    /**
     458    * @return If the node is the end of exactly one way, return this.
     459     *  <code>null</code> otherwise.
     460    */
     461    public static Way getWayForNode(Node n) {
     462        Way way = null;
     463        for (Way w : Main.ds.ways) {
     464            if (w.deleted || w.incomplete || w.nodes.size() < 1) continue;
     465            Node firstNode = w.nodes.get(0);
     466            Node lastNode = w.nodes.get(w.nodes.size() - 1);
     467            if ((firstNode == n || lastNode == n) && (firstNode != lastNode)) {
     468                if (way != null)
     469                    return null;
     470                way = w;
     471            }
     472        }
     473        return way;
     474    }
     475
     476    private static void pruneSuccsAndReverse(List<Integer> is) {
     477        //if (is.size() < 2) return;
     478
     479        HashSet<Integer> is2 = new HashSet<Integer>();
     480        for (int i : is) {
     481            if (!is2.contains(i - 1) && !is2.contains(i + 1)) {
     482                is2.add(i);
     483            }
     484        }
     485        is.clear();
     486        is.addAll(is2);
     487        Collections.sort(is);
     488        Collections.reverse(is);
     489    }
     490
     491    /**
     492    * Adjusts the position of a node to lie on a segment (or a segment
     493    * intersection).
     494    *
     495    * If one or more than two segments are passed, the node is adjusted
     496    * to lie on the first segment that is passed.
     497    *
     498    * If two segments are passed, the node is adjusted to be at their
     499    * intersection.
     500    *
     501    * No action is taken if no segments are passed.
     502    *
     503    * @param segs the segments to use as a reference when adjusting
     504    * @param n the node to adjust
     505    */
     506    private static void adjustNode(Collection<Pair<Node,Node>> segs, Node n) {
     507
     508        switch (segs.size()) {
     509        case 0:
     510            return;
     511        case 2:
     512            // This computes the intersection between
     513            // the two segments and adjusts the node position.
     514            Iterator<Pair<Node,Node>> i = segs.iterator();
     515            Pair<Node,Node> seg = i.next();
     516            EastNorth A = seg.a.eastNorth;
     517            EastNorth B = seg.b.eastNorth;
     518            seg = i.next();
     519            EastNorth C = seg.a.eastNorth;
     520            EastNorth D = seg.b.eastNorth;
     521
     522            double u=det(B.east() - A.east(), B.north() - A.north(), C.east() - D.east(), C.north() - D.north());
     523
     524            // Check for parallel segments and do nothing if they are
     525            // In practice this will probably only happen when a way has been duplicated
     526
     527            if (u == 0) return;
     528
     529            // q is a number between 0 and 1
     530            // It is the point in the segment where the intersection occurs
     531            // if the segment is scaled to lenght 1
     532
     533            double q = det(B.north() - C.north(), B.east() - C.east(), D.north() - C.north(), D.east() - C.east()) / u;
     534            EastNorth intersection = new EastNorth(
     535                    B.east() + q * (A.east() - B.east()),
     536                    B.north() + q * (A.north() - B.north()));
     537
     538            int snapToIntersectionThreshold=0;
     539            try { snapToIntersectionThreshold = Integer.parseInt(Main.pref.get("edit.snap-intersection-threshold","10")); } catch (NumberFormatException x) {}
     540
     541            // only adjust to intersection if within snapToIntersectionThreshold pixel of mouse click; otherwise
     542            // fall through to default action.
     543            // (for semi-parallel lines, intersection might be miles away!)
     544            if (Main.map.mapView.getPoint(n.eastNorth).distance(Main.map.mapView.getPoint(intersection)) < snapToIntersectionThreshold) {
     545                n.eastNorth = intersection;
     546                return;
     547            }
     548
     549        default:
     550            EastNorth P = n.eastNorth;
     551            seg = segs.iterator().next();
     552            A = seg.a.eastNorth;
     553            B = seg.b.eastNorth;
     554            double a = P.distanceSq(B);
     555            double b = P.distanceSq(A);
     556            double c = A.distanceSq(B);
    557557            q = (a - b + c) / (2*c);
    558                         n.eastNorth = new EastNorth(
    559                                 B.east() + q * (A.east() - B.east()),
    560                                 B.north() + q * (A.north() - B.north()));
    561                 }
    562         }
    563 
    564         // helper for adjustNode
    565         static double det(double a, double b, double c, double d)
    566         {
    567                 return a * d - b * c;
    568         }
    569 
    570         public void paint(Graphics g, MapView mv) {
    571 
    572                 // don't draw line if disabled in prefs
    573                 if (!drawHelperLine) return;
    574 
    575                 // sanity checks
    576                 if (Main.map.mapView == null) return;
    577                 if (mousePos == null) return;
    578 
    579                 // if shift key is held ("no auto-connect"), don't draw a line
    580                 if (shift) return;
    581 
    582                 // don't draw line if we don't know where from or where to
    583                 if (currentBaseNode == null) return;
    584                 if (currentMouseEastNorth == null) return;
    585 
    586                 // don't draw line if mouse is outside window
    587                 if (!Main.map.mapView.getBounds().contains(mousePos)) return;
    588 
    589                 Graphics2D g2 = (Graphics2D) g;
    590                 g2.setColor(selectedColor);
    591                 g2.setStroke(new BasicStroke(3, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
    592                 GeneralPath b = new GeneralPath();
    593                 Point p1=mv.getPoint(currentBaseNode.eastNorth);
    594                 Point p2=mv.getPoint(currentMouseEastNorth);
    595 
    596                 double t = Math.atan2(p2.y-p1.y, p2.x-p1.x) + Math.PI;
    597 
    598                 b.moveTo(p1.x,p1.y); b.lineTo(p2.x, p2.y);
    599 
    600                 // if alt key is held ("start new way"), draw a little perpendicular line
    601                 if (alt) {
    602                         b.moveTo((int)(p1.x + 8*Math.cos(t+PHI)), (int)(p1.y + 8*Math.sin(t+PHI)));
    603                         b.lineTo((int)(p1.x + 8*Math.cos(t-PHI)), (int)(p1.y + 8*Math.sin(t-PHI)));
    604                 }
    605 
    606                 g2.draw(b);
    607                 g2.setStroke(new BasicStroke(1));
    608 
    609         }
    610 
    611         @Override public String getModeHelpText() {
    612                 String rv;
    613 
    614                 if (currentBaseNode != null && !shift) {
    615                         if (mouseOnExistingNode) {
    616                                 if (alt && /* FIXME: way exists */true)
    617                                     rv = tr("Click to create a new way to the existing node.");
    618                                 else
    619                                         rv =tr("Click to make a connection to the existing node.");
    620                         } else {
    621                                 if (alt && /* FIXME: way exists */true)
    622                                     rv = tr("Click to insert a node and create a new way.");
    623                                 else
    624                                         rv = tr("Click to insert a new node and make a connection.");
    625                         }
    626                 }
    627                 else {
    628                         rv = tr("Click to insert a new node.");
    629                 }
    630 
    631                 //rv.append(tr("Click to add a new node. Ctrl: no node re-use/auto-insert. Shift: no auto-connect. Alt: new way"));
    632                 //rv.append(tr("Click to add a new node. Ctrl: no node re-use/auto-insert. Shift: no auto-connect. Alt: new way"));
    633                 return rv.toString();
    634         }
     558            n.eastNorth = new EastNorth(
     559                B.east() + q * (A.east() - B.east()),
     560                B.north() + q * (A.north() - B.north()));
     561        }
     562    }
     563
     564    // helper for adjustNode
     565    static double det(double a, double b, double c, double d)
     566    {
     567        return a * d - b * c;
     568    }
     569
     570    public void paint(Graphics g, MapView mv) {
     571
     572        // don't draw line if disabled in prefs
     573        if (!drawHelperLine) return;
     574
     575        // sanity checks
     576        if (Main.map.mapView == null) return;
     577        if (mousePos == null) return;
     578
     579        // if shift key is held ("no auto-connect"), don't draw a line
     580        if (shift) return;
     581
     582        // don't draw line if we don't know where from or where to
     583        if (currentBaseNode == null) return;
     584        if (currentMouseEastNorth == null) return;
     585
     586        // don't draw line if mouse is outside window
     587        if (!Main.map.mapView.getBounds().contains(mousePos)) return;
     588
     589        Graphics2D g2 = (Graphics2D) g;
     590        g2.setColor(selectedColor);
     591        g2.setStroke(new BasicStroke(3, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
     592        GeneralPath b = new GeneralPath();
     593        Point p1=mv.getPoint(currentBaseNode.eastNorth);
     594        Point p2=mv.getPoint(currentMouseEastNorth);
     595
     596        double t = Math.atan2(p2.y-p1.y, p2.x-p1.x) + Math.PI;
     597
     598        b.moveTo(p1.x,p1.y); b.lineTo(p2.x, p2.y);
     599
     600        // if alt key is held ("start new way"), draw a little perpendicular line
     601        if (alt) {
     602            b.moveTo((int)(p1.x + 8*Math.cos(t+PHI)), (int)(p1.y + 8*Math.sin(t+PHI)));
     603            b.lineTo((int)(p1.x + 8*Math.cos(t-PHI)), (int)(p1.y + 8*Math.sin(t-PHI)));
     604        }
     605
     606        g2.draw(b);
     607        g2.setStroke(new BasicStroke(1));
     608
     609    }
     610
     611    @Override public String getModeHelpText() {
     612        String rv;
     613
     614        if (currentBaseNode != null && !shift) {
     615            if (mouseOnExistingNode) {
     616                if (alt && /* FIXME: way exists */true)
     617                    rv = tr("Click to create a new way to the existing node.");
     618                else
     619                    rv =tr("Click to make a connection to the existing node.");
     620            } else {
     621                if (alt && /* FIXME: way exists */true)
     622                    rv = tr("Click to insert a node and create a new way.");
     623                else
     624                    rv = tr("Click to insert a new node and make a connection.");
     625            }
     626        }
     627        else {
     628            rv = tr("Click to insert a new node.");
     629        }
     630
     631        //rv.append(tr("Click to add a new node. Ctrl: no node re-use/auto-insert. Shift: no auto-connect. Alt: new way"));
     632        //rv.append(tr("Click to add a new node. Ctrl: no node re-use/auto-insert. Shift: no auto-connect. Alt: new way"));
     633        return rv.toString();
     634    }
    635635}
  • trunk/src/org/openstreetmap/josm/actions/mapmode/ExtrudeAction.java

    r1158 r1169  
    4040public class ExtrudeAction extends MapMode implements MapViewPaintable {
    4141
    42         enum Mode { EXTRUDE, rotate, select }
    43         private Mode mode = null;
    44         private long mouseDownTime = 0;
    45         private WaySegment selectedSegment = null;
    46         private Color selectedColor;
    47 
    48         double xoff;
    49         double yoff;
    50         double distance;
    51 
    52         /**
    53         * The old cursor before the user pressed the mouse button.
    54         */
    55         private Cursor oldCursor;
    56         /**
    57         * The current position of the mouse
    58         */
    59         private Point mousePos;
    60         /**
    61         * The position of the mouse cursor when the drag action was initiated.
    62         */
    63         private Point initialMousePos;
    64         /**
    65         * The time which needs to pass between click and release before something
    66         * counts as a move, in milliseconds
    67         */
    68         private int initialMoveDelay = 200;
    69 
    70         /**
    71         * Create a new SelectAction
    72         * @param mapFrame The MapFrame this action belongs to.
    73         */
    74         public ExtrudeAction(MapFrame mapFrame) {
    75                 super(tr("Extrude"), "extrude/extrude", tr("Create areas"),
    76                                 Shortcut.registerShortcut("mapmode:extrude", tr("Mode: {0}", tr("Extrude")), KeyEvent.VK_X, Shortcut.GROUP_EDIT),
    77                         mapFrame,
    78                         getCursor("normal", "rectangle", Cursor.DEFAULT_CURSOR));
    79                 putValue("help", "Action/Extrude/Extrude");
    80                 initialMoveDelay = Main.pref.getInteger("edit.initial-move-delay",200);
    81                 selectedColor = Main.pref.getColor(marktr("selected"), Color.YELLOW);
    82         }
    83 
    84         private static Cursor getCursor(String name, String mod, int def) {
    85                 try {
    86                 return ImageProvider.getCursor(name, mod);
     42    enum Mode { EXTRUDE, rotate, select }
     43    private Mode mode = null;
     44    private long mouseDownTime = 0;
     45    private WaySegment selectedSegment = null;
     46    private Color selectedColor;
     47
     48    double xoff;
     49    double yoff;
     50    double distance;
     51
     52    /**
     53    * The old cursor before the user pressed the mouse button.
     54    */
     55    private Cursor oldCursor;
     56    /**
     57    * The current position of the mouse
     58    */
     59    private Point mousePos;
     60    /**
     61    * The position of the mouse cursor when the drag action was initiated.
     62    */
     63    private Point initialMousePos;
     64    /**
     65    * The time which needs to pass between click and release before something
     66    * counts as a move, in milliseconds
     67    */
     68    private int initialMoveDelay = 200;
     69
     70    /**
     71    * Create a new SelectAction
     72    * @param mapFrame The MapFrame this action belongs to.
     73    */
     74    public ExtrudeAction(MapFrame mapFrame) {
     75        super(tr("Extrude"), "extrude/extrude", tr("Create areas"),
     76                Shortcut.registerShortcut("mapmode:extrude", tr("Mode: {0}", tr("Extrude")), KeyEvent.VK_X, Shortcut.GROUP_EDIT),
     77            mapFrame,
     78            getCursor("normal", "rectangle", Cursor.DEFAULT_CURSOR));
     79        putValue("help", "Action/Extrude/Extrude");
     80        initialMoveDelay = Main.pref.getInteger("edit.initial-move-delay",200);
     81        selectedColor = Main.pref.getColor(marktr("selected"), Color.YELLOW);
     82    }
     83
     84    private static Cursor getCursor(String name, String mod, int def) {
     85        try {
     86            return ImageProvider.getCursor(name, mod);
    8787        } catch (Exception e) {
    8888        }
    89             return Cursor.getPredefinedCursor(def);
    90     }
    91 
    92         private void setCursor(Cursor c) {
    93                 if (oldCursor == null) {
    94                         oldCursor = Main.map.mapView.getCursor();
    95                         Main.map.mapView.setCursor(c);
    96                 }
    97         }
    98 
    99         private void restoreCursor() {
    100                 if (oldCursor != null) {
    101                         Main.map.mapView.setCursor(oldCursor);
    102                         oldCursor = null;
    103                 }
    104         }
    105 
    106         @Override public void enterMode() {
    107                 super.enterMode();
    108                 Main.map.mapView.addMouseListener(this);
    109                 Main.map.mapView.addMouseMotionListener(this);
    110         }
    111 
    112         @Override public void exitMode() {
    113                 super.exitMode();
    114                 Main.map.mapView.removeMouseListener(this);
    115                 Main.map.mapView.removeMouseMotionListener(this);
    116                 Main.map.mapView.removeTemporaryLayer(this);
    117 
    118         }
    119 
    120         /**
    121         * If the left mouse button is pressed, move all currently selected
    122         * objects (if one of them is under the mouse) or the current one under the
    123         * mouse (which will become selected).
    124         */
    125         @Override public void mouseDragged(MouseEvent e) {
    126                 if (mode == Mode.select) return;
    127 
    128                 // do not count anything as a move if it lasts less than 100 milliseconds.
    129                 if ((mode == Mode.EXTRUDE) && (System.currentTimeMillis() - mouseDownTime < initialMoveDelay)) return;
    130 
    131                 if ((e.getModifiersEx() & MouseEvent.BUTTON1_DOWN_MASK) == 0)
    132                         return;
    133 
    134                 if (mode == Mode.EXTRUDE) {
    135                         setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));
    136                 }
    137 
    138                 if (mousePos == null) {
    139                         mousePos = e.getPoint();
    140                         return;
    141                 }
    142 
    143                 Main.map.mapView.repaint();
    144                 mousePos = e.getPoint();
    145 
    146         }
    147 
    148         public void paint(Graphics g, MapView mv) {
    149                 if (selectedSegment != null) {
    150                         Node n1 = selectedSegment.way.nodes.get(selectedSegment.lowerIndex);
    151                         Node n2 = selectedSegment.way.nodes.get(selectedSegment.lowerIndex+1);
    152 
    153                         EastNorth en1 = n1.eastNorth;
    154                         EastNorth en2 = n2.eastNorth;
    155                         if (en1.east() < en2.east()) { en2 = en1; en1 = n2.eastNorth; }
    156                         EastNorth en3 = mv.getEastNorth(mousePos.x, mousePos.y);
    157 
    158                         double u = ((en3.east()-en1.east())*(en2.east()-en1.east()) + (en3.north()-en1.north())*(en2.north()-en1.north()))/en2.distanceSq(en1);
    159                         // the point on the segment from which the distance to mouse pos is shortest
    160                         EastNorth base = new EastNorth(en1.east()+u*(en2.east()-en1.east()), en1.north()+u*(en2.north()-en1.north()));
    161 
    162                         // the distance, in projection units, between the base point and the mouse cursor
    163                         double len = base.distance(en3);
    164 
    165                         // find out the distance, in metres, between the base point and the mouse cursor
    166                         distance = Main.proj.eastNorth2latlon(base).greatCircleDistance(Main.proj.eastNorth2latlon(en3));
    167                         Main.map.statusLine.setDist(distance);
    168                         updateStatusLine();
    169 
    170                         // compute the angle at which the segment is drawn
    171                         // and use it to compute the x and y offsets for the
    172                         // corner points.
    173                         double sin_alpha = (en2.north()-en1.north())/en2.distance(en1);
    174 
    175                         // this is a kludge because sometimes extrusion just goes the wrong direction
    176                         if ((en3.east()>base.east()) ^ (sin_alpha < 0)) len=-len;
    177                         xoff = sin_alpha * len;
    178                         yoff = Math.sqrt(1-sin_alpha*sin_alpha) * len;
    179 
    180                         Graphics2D g2 = (Graphics2D) g;
    181                         g2.setColor(selectedColor);
    182                         g2.setStroke(new BasicStroke(3, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
    183                         GeneralPath b = new GeneralPath();
    184                         Point p1=mv.getPoint(en1);
    185                         Point p2=mv.getPoint(en2);
    186                         Point p3=mv.getPoint(en1.add(-xoff, -yoff));
    187                         Point p4=mv.getPoint(en2.add(-xoff, -yoff));
    188 
    189                         b.moveTo(p1.x,p1.y); b.lineTo(p3.x, p3.y);
    190                         b.lineTo(p4.x, p4.y); b.lineTo(p2.x, p2.y);
    191                         b.lineTo(p1.x,p1.y);
    192                         g2.draw(b);
    193                         g2.setStroke(new BasicStroke(1));
    194                 }
    195         }
    196 
    197         /**
    198         */
    199         @Override public void mousePressed(MouseEvent e) {
    200                 if (!(Boolean)this.getValue("active")) return;
    201                 if (e.getButton() != MouseEvent.BUTTON1)
    202                         return;
    203                 // boolean ctrl = (e.getModifiers() & ActionEvent.CTRL_MASK) != 0;
    204                 // boolean alt = (e.getModifiers() & ActionEvent.ALT_MASK) != 0;
    205                 // boolean shift = (e.getModifiers() & ActionEvent.SHIFT_MASK) != 0;
    206 
    207                 mouseDownTime = System.currentTimeMillis();
    208 
    209                 selectedSegment =
    210                         Main.map.mapView.getNearestWaySegment(e.getPoint());
    211 
    212                 mode = (selectedSegment == null) ? Mode.select : Mode.EXTRUDE;
    213                 oldCursor = Main.map.mapView.getCursor();
    214 
    215                 updateStatusLine();
    216                 Main.map.mapView.addTemporaryLayer(this);
    217                 Main.map.mapView.repaint();
    218 
    219                 mousePos = e.getPoint();
    220                 initialMousePos = e.getPoint();
    221         }
    222 
    223         /**
    224         * Restore the old mouse cursor.
    225         */
    226         @Override public void mouseReleased(MouseEvent e) {
    227                 restoreCursor();
    228                 if (selectedSegment == null) return;
    229                 if (mousePos.distance(initialMousePos) > 10) {
    230                         Node n1 = selectedSegment.way.nodes.get(selectedSegment.lowerIndex);
    231                         Node n2 = selectedSegment.way.nodes.get(selectedSegment.lowerIndex+1);
    232                         EastNorth en3 = n2.eastNorth.add(-xoff, -yoff);
    233                         Node n3 = new Node(Main.proj.eastNorth2latlon(en3));
    234                         EastNorth en4 = n1.eastNorth.add(-xoff, -yoff);
    235                         Node n4 = new Node(Main.proj.eastNorth2latlon(en4));
    236                         Way wnew = new Way(selectedSegment.way);
    237                         wnew.nodes.add(selectedSegment.lowerIndex+1, n3);
    238                         wnew.nodes.add(selectedSegment.lowerIndex+1, n4);
    239                         if (wnew.nodes.size() == 4) wnew.nodes.add(n1);
    240                         Collection<Command> cmds = new LinkedList<Command>();
    241                         cmds.add(new AddCommand(n4));
    242                         cmds.add(new AddCommand(n3));
    243                         cmds.add(new ChangeCommand(selectedSegment.way, wnew));
    244                         Command c = new SequenceCommand(tr("Extrude Way"), cmds);
    245                         Main.main.undoRedo.add(c);
    246                 }
    247 
    248                 Main.map.mapView.removeTemporaryLayer(this);
    249                 selectedSegment = null;
    250                 mode = null;
    251                 updateStatusLine();
    252                 Main.map.mapView.repaint();
    253         }
    254 
    255         @Override public String getModeHelpText() {
    256                 if (mode == Mode.select) {
    257                         return tr("Release the mouse button to select the objects in the rectangle.");
    258                 } else if (mode == Mode.EXTRUDE) {
    259                         return tr("Draw a rectangle of the desired size, then release the mouse button.");
    260                 } else if (mode == Mode.rotate) {
    261                         return tr("Release the mouse button to stop rotating.");
    262                 } else {
    263                         return tr("Drag a way segment to make a rectangle.");
    264                 }
    265         }
     89        return Cursor.getPredefinedCursor(def);
     90    }
     91
     92    private void setCursor(Cursor c) {
     93        if (oldCursor == null) {
     94            oldCursor = Main.map.mapView.getCursor();
     95            Main.map.mapView.setCursor(c);
     96        }
     97    }
     98
     99    private void restoreCursor() {
     100        if (oldCursor != null) {
     101            Main.map.mapView.setCursor(oldCursor);
     102            oldCursor = null;
     103        }
     104    }
     105
     106    @Override public void enterMode() {
     107        super.enterMode();
     108        Main.map.mapView.addMouseListener(this);
     109        Main.map.mapView.addMouseMotionListener(this);
     110    }
     111
     112    @Override public void exitMode() {
     113        super.exitMode();
     114        Main.map.mapView.removeMouseListener(this);
     115        Main.map.mapView.removeMouseMotionListener(this);
     116        Main.map.mapView.removeTemporaryLayer(this);
     117
     118    }
     119
     120    /**
     121    * If the left mouse button is pressed, move all currently selected
     122    * objects (if one of them is under the mouse) or the current one under the
     123    * mouse (which will become selected).
     124    */
     125    @Override public void mouseDragged(MouseEvent e) {
     126        if (mode == Mode.select) return;
     127
     128        // do not count anything as a move if it lasts less than 100 milliseconds.
     129        if ((mode == Mode.EXTRUDE) && (System.currentTimeMillis() - mouseDownTime < initialMoveDelay)) return;
     130
     131        if ((e.getModifiersEx() & MouseEvent.BUTTON1_DOWN_MASK) == 0)
     132            return;
     133
     134        if (mode == Mode.EXTRUDE) {
     135            setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));
     136        }
     137
     138        if (mousePos == null) {
     139            mousePos = e.getPoint();
     140            return;
     141        }
     142
     143        Main.map.mapView.repaint();
     144        mousePos = e.getPoint();
     145
     146    }
     147
     148    public void paint(Graphics g, MapView mv) {
     149        if (selectedSegment != null) {
     150            Node n1 = selectedSegment.way.nodes.get(selectedSegment.lowerIndex);
     151            Node n2 = selectedSegment.way.nodes.get(selectedSegment.lowerIndex+1);
     152
     153            EastNorth en1 = n1.eastNorth;
     154            EastNorth en2 = n2.eastNorth;
     155            if (en1.east() < en2.east()) { en2 = en1; en1 = n2.eastNorth; }
     156            EastNorth en3 = mv.getEastNorth(mousePos.x, mousePos.y);
     157
     158            double u = ((en3.east()-en1.east())*(en2.east()-en1.east()) + (en3.north()-en1.north())*(en2.north()-en1.north()))/en2.distanceSq(en1);
     159            // the point on the segment from which the distance to mouse pos is shortest
     160            EastNorth base = new EastNorth(en1.east()+u*(en2.east()-en1.east()), en1.north()+u*(en2.north()-en1.north()));
     161
     162            // the distance, in projection units, between the base point and the mouse cursor
     163            double len = base.distance(en3);
     164
     165            // find out the distance, in metres, between the base point and the mouse cursor
     166            distance = Main.proj.eastNorth2latlon(base).greatCircleDistance(Main.proj.eastNorth2latlon(en3));
     167            Main.map.statusLine.setDist(distance);
     168            updateStatusLine();
     169
     170            // compute the angle at which the segment is drawn
     171            // and use it to compute the x and y offsets for the
     172            // corner points.
     173            double sin_alpha = (en2.north()-en1.north())/en2.distance(en1);
     174
     175            // this is a kludge because sometimes extrusion just goes the wrong direction
     176            if ((en3.east()>base.east()) ^ (sin_alpha < 0)) len=-len;
     177            xoff = sin_alpha * len;
     178            yoff = Math.sqrt(1-sin_alpha*sin_alpha) * len;
     179
     180            Graphics2D g2 = (Graphics2D) g;
     181            g2.setColor(selectedColor);
     182            g2.setStroke(new BasicStroke(3, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
     183            GeneralPath b = new GeneralPath();
     184            Point p1=mv.getPoint(en1);
     185            Point p2=mv.getPoint(en2);
     186            Point p3=mv.getPoint(en1.add(-xoff, -yoff));
     187            Point p4=mv.getPoint(en2.add(-xoff, -yoff));
     188
     189            b.moveTo(p1.x,p1.y); b.lineTo(p3.x, p3.y);
     190            b.lineTo(p4.x, p4.y); b.lineTo(p2.x, p2.y);
     191            b.lineTo(p1.x,p1.y);
     192            g2.draw(b);
     193            g2.setStroke(new BasicStroke(1));
     194        }
     195    }
     196
     197    /**
     198    */
     199    @Override public void mousePressed(MouseEvent e) {
     200        if (!(Boolean)this.getValue("active")) return;
     201        if (e.getButton() != MouseEvent.BUTTON1)
     202            return;
     203        // boolean ctrl = (e.getModifiers() & ActionEvent.CTRL_MASK) != 0;
     204        // boolean alt = (e.getModifiers() & ActionEvent.ALT_MASK) != 0;
     205        // boolean shift = (e.getModifiers() & ActionEvent.SHIFT_MASK) != 0;
     206
     207        mouseDownTime = System.currentTimeMillis();
     208
     209        selectedSegment =
     210            Main.map.mapView.getNearestWaySegment(e.getPoint());
     211
     212        mode = (selectedSegment == null) ? Mode.select : Mode.EXTRUDE;
     213        oldCursor = Main.map.mapView.getCursor();
     214
     215        updateStatusLine();
     216        Main.map.mapView.addTemporaryLayer(this);
     217        Main.map.mapView.repaint();
     218
     219        mousePos = e.getPoint();
     220        initialMousePos = e.getPoint();
     221    }
     222
     223    /**
     224    * Restore the old mouse cursor.
     225    */
     226    @Override public void mouseReleased(MouseEvent e) {
     227        restoreCursor();
     228        if (selectedSegment == null) return;
     229        if (mousePos.distance(initialMousePos) > 10) {
     230            Node n1 = selectedSegment.way.nodes.get(selectedSegment.lowerIndex);
     231            Node n2 = selectedSegment.way.nodes.get(selectedSegment.lowerIndex+1);
     232            EastNorth en3 = n2.eastNorth.add(-xoff, -yoff);
     233            Node n3 = new Node(Main.proj.eastNorth2latlon(en3));
     234            EastNorth en4 = n1.eastNorth.add(-xoff, -yoff);
     235            Node n4 = new Node(Main.proj.eastNorth2latlon(en4));
     236            Way wnew = new Way(selectedSegment.way);
     237            wnew.nodes.add(selectedSegment.lowerIndex+1, n3);
     238            wnew.nodes.add(selectedSegment.lowerIndex+1, n4);
     239            if (wnew.nodes.size() == 4) wnew.nodes.add(n1);
     240            Collection<Command> cmds = new LinkedList<Command>();
     241            cmds.add(new AddCommand(n4));
     242            cmds.add(new AddCommand(n3));
     243            cmds.add(new ChangeCommand(selectedSegment.way, wnew));
     244            Command c = new SequenceCommand(tr("Extrude Way"), cmds);
     245            Main.main.undoRedo.add(c);
     246        }
     247
     248        Main.map.mapView.removeTemporaryLayer(this);
     249        selectedSegment = null;
     250        mode = null;
     251        updateStatusLine();
     252        Main.map.mapView.repaint();
     253    }
     254
     255    @Override public String getModeHelpText() {
     256        if (mode == Mode.select) {
     257            return tr("Release the mouse button to select the objects in the rectangle.");
     258        } else if (mode == Mode.EXTRUDE) {
     259            return tr("Draw a rectangle of the desired size, then release the mouse button.");
     260        } else if (mode == Mode.rotate) {
     261            return tr("Release the mouse button to stop rotating.");
     262        } else {
     263            return tr("Drag a way segment to make a rectangle.");
     264        }
     265    }
    266266}
  • trunk/src/org/openstreetmap/josm/actions/mapmode/MapMode.java

    r1084 r1169  
    2323 */
    2424abstract public class MapMode extends JosmAction implements MouseListener, MouseMotionListener {
    25         private final Cursor cursor;
    26         private Cursor oldCursor;
     25    private final Cursor cursor;
     26    private Cursor oldCursor;
    2727
    28         /**
    29         * Constructor for mapmodes without an menu
    30         */
    31         public MapMode(String name, String iconName, String tooltip, Shortcut shortcut, MapFrame mapFrame, Cursor cursor) {
    32                 super(name, "mapmode/"+iconName, tooltip, shortcut, false);
    33                 this.cursor = cursor;
    34                 putValue("active", false);
    35         }
     28    /**
     29    * Constructor for mapmodes without an menu
     30    */
     31    public MapMode(String name, String iconName, String tooltip, Shortcut shortcut, MapFrame mapFrame, Cursor cursor) {
     32        super(name, "mapmode/"+iconName, tooltip, shortcut, false);
     33        this.cursor = cursor;
     34        putValue("active", false);
     35    }
    3636
    37         /**
    38         * Constructor for mapmodes without an menu
    39         */
    40         @Deprecated
    41         public MapMode(String name, String iconName, String tooltip, int keystroke, MapFrame mapFrame, Cursor cursor) {
    42                 super(name, "mapmode/"+iconName, tooltip, keystroke, 0, false);
    43                 this.cursor = cursor;
    44                 putValue("active", false);
    45         }
     37    /**
     38    * Constructor for mapmodes without an menu
     39    */
     40    @Deprecated
     41    public MapMode(String name, String iconName, String tooltip, int keystroke, MapFrame mapFrame, Cursor cursor) {
     42        super(name, "mapmode/"+iconName, tooltip, keystroke, 0, false);
     43        this.cursor = cursor;
     44        putValue("active", false);
     45    }
    4646
    47         /**
    48         * Constructor for mapmodes with an menu (no shortcut will be registered)
    49         */
    50         public MapMode(String name, String iconName, String tooltip, MapFrame mapFrame, Cursor cursor) {
    51                 putValue(NAME, name);
    52                 putValue(SMALL_ICON, ImageProvider.get("mapmode", iconName));
    53                 putValue(SHORT_DESCRIPTION, tooltip);
    54                 this.cursor = cursor;
    55         }
     47    /**
     48    * Constructor for mapmodes with an menu (no shortcut will be registered)
     49    */
     50    public MapMode(String name, String iconName, String tooltip, MapFrame mapFrame, Cursor cursor) {
     51        putValue(NAME, name);
     52        putValue(SMALL_ICON, ImageProvider.get("mapmode", iconName));
     53        putValue(SHORT_DESCRIPTION, tooltip);
     54        this.cursor = cursor;
     55    }
    5656
    57         public void enterMode() {
    58                 putValue("active", true);
    59                 oldCursor = Main.map.mapView.getCursor();
    60                 Main.map.mapView.setCursor(cursor);
    61                 updateStatusLine();
    62         }
    63         public void exitMode() {
    64                 putValue("active", false);
    65                 Main.map.mapView.setCursor(oldCursor);
    66         }
     57    public void enterMode() {
     58        putValue("active", true);
     59        oldCursor = Main.map.mapView.getCursor();
     60        Main.map.mapView.setCursor(cursor);
     61        updateStatusLine();
     62    }
     63    public void exitMode() {
     64        putValue("active", false);
     65        Main.map.mapView.setCursor(oldCursor);
     66    }
    6767
    68         protected void updateStatusLine() {
    69                 Main.map.statusLine.setHelpText(getModeHelpText());
    70                 Main.map.statusLine.repaint();
    71         }
     68    protected void updateStatusLine() {
     69        Main.map.statusLine.setHelpText(getModeHelpText());
     70        Main.map.statusLine.repaint();
     71    }
    7272
    73         public String getModeHelpText() {
    74                 return "";
    75         }
    76         /**
    77         * Call selectMapMode(this) on the parent mapFrame.
    78         */
    79         public void actionPerformed(ActionEvent e) {
    80                 if (Main.map != null)
    81                         Main.map.selectMapMode(this);
    82         }
     73    public String getModeHelpText() {
     74        return "";
     75    }
     76    /**
     77    * Call selectMapMode(this) on the parent mapFrame.
     78    */
     79    public void actionPerformed(ActionEvent e) {
     80        if (Main.map != null)
     81            Main.map.selectMapMode(this);
     82    }
    8383
    84         public void mouseReleased(MouseEvent e) {}
    85         public void mouseExited(MouseEvent e) {}
    86         public void mousePressed(MouseEvent e) {}
    87         public void mouseClicked(MouseEvent e) {}
    88         public void mouseEntered(MouseEvent e) {}
    89         public void mouseMoved(MouseEvent e) {}
    90         public void mouseDragged(MouseEvent e) {}
     84    public void mouseReleased(MouseEvent e) {}
     85    public void mouseExited(MouseEvent e) {}
     86    public void mousePressed(MouseEvent e) {}
     87    public void mouseClicked(MouseEvent e) {}
     88    public void mouseEntered(MouseEvent e) {}
     89    public void mouseMoved(MouseEvent e) {}
     90    public void mouseDragged(MouseEvent e) {}
    9191}
  • trunk/src/org/openstreetmap/josm/actions/mapmode/PlayHeadDragMode.java

    r1047 r1169  
    2121public class PlayHeadDragMode extends MapMode {
    2222
    23         private boolean dragging = false;
    24         private Point mousePos = null;
    25         private Point mouseStart = null;
    26         private PlayHeadMarker playHeadMarker = null;
     23    private boolean dragging = false;
     24    private Point mousePos = null;
     25    private Point mouseStart = null;
     26    private PlayHeadMarker playHeadMarker = null;
    2727
    28         public PlayHeadDragMode(PlayHeadMarker m) {
    29                 super("play head drag", "playheaddrag", "play head drag", null,
    30                 Main.map, Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));
    31                 playHeadMarker = m;
    32         }
     28    public PlayHeadDragMode(PlayHeadMarker m) {
     29        super("play head drag", "playheaddrag", "play head drag", null,
     30        Main.map, Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));
     31        playHeadMarker = m;
     32    }
    3333
    34         @Override public void enterMode() {
    35                 super.enterMode();
    36                 Main.map.mapView.addMouseListener(this);
    37                 Main.map.mapView.addMouseMotionListener(this);
    38         }
     34    @Override public void enterMode() {
     35        super.enterMode();
     36        Main.map.mapView.addMouseListener(this);
     37        Main.map.mapView.addMouseMotionListener(this);
     38    }
    3939
    40         @Override public void exitMode() {
    41                 super.exitMode();
    42                 Main.map.mapView.removeMouseListener(this);
    43                 Main.map.mapView.removeMouseMotionListener(this);
    44         }
     40    @Override public void exitMode() {
     41        super.exitMode();
     42        Main.map.mapView.removeMouseListener(this);
     43        Main.map.mapView.removeMouseMotionListener(this);
     44    }
    4545
    46         @Override public void mousePressed(MouseEvent ev) {
    47                 mouseStart = mousePos = ev.getPoint();
    48         }
     46    @Override public void mousePressed(MouseEvent ev) {
     47        mouseStart = mousePos = ev.getPoint();
     48    }
    4949
    50         @Override public void mouseDragged(MouseEvent ev) {
    51                 if (mouseStart == null || mousePos == null) return;
    52                 if ((ev.getModifiersEx() & MouseEvent.BUTTON1_DOWN_MASK) == 0) return;
    53                 Point p = ev.getPoint();
    54                 if (p == null) return;
    55                 if (! dragging) {
    56                         if (p.distance(mouseStart) < 3) return;
    57                         playHeadMarker.startDrag();
    58                         dragging = true;
    59                 }
    60                 if (p.distance(mousePos) == 0) return;
    61                 playHeadMarker.drag(Main.map.mapView.getEastNorth(ev.getX(), ev.getY()));
    62                 mousePos = p;
    63         }
     50    @Override public void mouseDragged(MouseEvent ev) {
     51        if (mouseStart == null || mousePos == null) return;
     52        if ((ev.getModifiersEx() & MouseEvent.BUTTON1_DOWN_MASK) == 0) return;
     53        Point p = ev.getPoint();
     54        if (p == null) return;
     55        if (! dragging) {
     56            if (p.distance(mouseStart) < 3) return;
     57            playHeadMarker.startDrag();
     58            dragging = true;
     59        }
     60        if (p.distance(mousePos) == 0) return;
     61        playHeadMarker.drag(Main.map.mapView.getEastNorth(ev.getX(), ev.getY()));
     62        mousePos = p;
     63    }
    6464
    65         @Override public void mouseReleased(MouseEvent ev) {
    66                 Point p = ev.getPoint();
    67                 mouseStart = null;
    68                 if (ev.getButton() != MouseEvent.BUTTON1 || p == null || ! dragging)
    69                         return;
    70                 boolean shift = (ev.getModifiers() & ActionEvent.SHIFT_MASK) != 0;
    71                 EastNorth en = Main.map.mapView.getEastNorth(ev.getX(), ev.getY());
    72                 if (! shift) {
    73                         playHeadMarker.reposition(en);
    74                 } else {
    75                         playHeadMarker.synchronize(en);
    76                 }
    77                 mousePos = null;
    78                 dragging = false;
     65    @Override public void mouseReleased(MouseEvent ev) {
     66        Point p = ev.getPoint();
     67        mouseStart = null;
     68        if (ev.getButton() != MouseEvent.BUTTON1 || p == null || ! dragging)
     69            return;
     70        boolean shift = (ev.getModifiers() & ActionEvent.SHIFT_MASK) != 0;
     71        EastNorth en = Main.map.mapView.getEastNorth(ev.getX(), ev.getY());
     72        if (! shift) {
     73            playHeadMarker.reposition(en);
     74        } else {
     75            playHeadMarker.synchronize(en);
     76        }
     77        mousePos = null;
     78        dragging = false;
    7979
    80         /*
    81                 boolean ctrl = (e.getModifiers() & ActionEvent.CTRL_MASK) != 0;
    82                 boolean alt = (e.getModifiers() & ActionEvent.ALT_MASK) != 0;
    83         */
    84         }
     80    /*
     81        boolean ctrl = (e.getModifiers() & ActionEvent.CTRL_MASK) != 0;
     82        boolean alt = (e.getModifiers() & ActionEvent.ALT_MASK) != 0;
     83    */
     84    }
    8585
    86         @Override public String getModeHelpText() {
    87                 return tr("Drag play head and release near track to play audio from there; SHIFT+release to synchronize audio at that point.");
    88         }
     86    @Override public String getModeHelpText() {
     87        return tr("Drag play head and release near track to play audio from there; SHIFT+release to synchronize audio at that point.");
     88    }
    8989}
  • trunk/src/org/openstreetmap/josm/actions/mapmode/ZoomAction.java

    r1084 r1169  
    3030public class ZoomAction extends MapMode implements SelectionEnded {
    3131
    32         /**
    33         * Shortcut to the mapview.
    34         */
    35         private final MapView mv;
    36         /**
    37         * Manager that manages the selection rectangle with the aspect ratio of the
    38         * MapView.
    39         */
    40         private final SelectionManager selectionManager;
     32    /**
     33    * Shortcut to the mapview.
     34    */
     35    private final MapView mv;
     36    /**
     37    * Manager that manages the selection rectangle with the aspect ratio of the
     38    * MapView.
     39    */
     40    private final SelectionManager selectionManager;
    4141
    4242
    43         /**
    44         * Construct a ZoomAction without a label.
    45         * @param mapFrame The MapFrame, whose zoom mode should be enabled.
    46         */
    47         public ZoomAction(MapFrame mapFrame) {
    48                 super(tr("Zoom"), "zoom", tr("Zoom and move map"),
    49                 Shortcut.registerShortcut("mapmode:zoom", tr("Mode: {0}", tr("Zoom")), KeyEvent.VK_Z, Shortcut.GROUP_EDIT),
    50                 mapFrame, ImageProvider.getCursor("normal", "zoom"));
    51                 mv = mapFrame.mapView;
    52                 selectionManager = new SelectionManager(this, true, mv);
    53         }
     43    /**
     44    * Construct a ZoomAction without a label.
     45    * @param mapFrame The MapFrame, whose zoom mode should be enabled.
     46    */
     47    public ZoomAction(MapFrame mapFrame) {
     48        super(tr("Zoom"), "zoom", tr("Zoom and move map"),
     49        Shortcut.registerShortcut("mapmode:zoom", tr("Mode: {0}", tr("Zoom")), KeyEvent.VK_Z, Shortcut.GROUP_EDIT),
     50        mapFrame, ImageProvider.getCursor("normal", "zoom"));
     51        mv = mapFrame.mapView;
     52        selectionManager = new SelectionManager(this, true, mv);
     53    }
    5454
    55         /**
    56         * Zoom to the rectangle on the map.
    57         */
    58         public void selectionEnded(Rectangle r, boolean alt, boolean shift, boolean ctrl) {
    59                 if (r.width >= 3 && r.height >= 3) {
    60                         double scale = mv.getScale() * r.getWidth()/mv.getWidth();
    61                         EastNorth newCenter = mv.getEastNorth(r.x+r.width/2, r.y+r.height/2);
    62                         mv.zoomTo(newCenter, scale);
    63                 }
    64         }
     55    /**
     56    * Zoom to the rectangle on the map.
     57    */
     58    public void selectionEnded(Rectangle r, boolean alt, boolean shift, boolean ctrl) {
     59        if (r.width >= 3 && r.height >= 3) {
     60            double scale = mv.getScale() * r.getWidth()/mv.getWidth();
     61            EastNorth newCenter = mv.getEastNorth(r.x+r.width/2, r.y+r.height/2);
     62            mv.zoomTo(newCenter, scale);
     63        }
     64    }
    6565
    66         @Override public void enterMode() {
    67                 super.enterMode();
    68                 selectionManager.register(mv);
    69         }
     66    @Override public void enterMode() {
     67        super.enterMode();
     68        selectionManager.register(mv);
     69    }
    7070
    71         @Override public void exitMode() {
    72                 super.exitMode();
    73                 selectionManager.unregister(mv);
    74         }
     71    @Override public void exitMode() {
     72        super.exitMode();
     73        selectionManager.unregister(mv);
     74    }
    7575
    76         @Override public String getModeHelpText() {
    77                 return tr("Zoom by dragging or Ctrl+. or Ctrl+,; move with Ctrl+up,left,down,right; move zoom with right button");
    78         }
     76    @Override public String getModeHelpText() {
     77        return tr("Zoom by dragging or Ctrl+. or Ctrl+,; move with Ctrl+up,left,down,right; move zoom with right button");
     78    }
    7979}
  • trunk/src/org/openstreetmap/josm/actions/search/PushbackTokenizer.java

    r1149 r1169  
    77
    88public class PushbackTokenizer {
    9         private PushbackReader search;
     9    private PushbackReader search;
    1010
    11         private LinkedList<String> pushBackBuf = new LinkedList<String>();
     11    private LinkedList<String> pushBackBuf = new LinkedList<String>();
    1212
    13         public PushbackTokenizer(PushbackReader search) {
    14                 this.search = search;
    15         }
     13    public PushbackTokenizer(PushbackReader search) {
     14        this.search = search;
     15    }
    1616
    17         /**
    18         * The token returned is <code>null</code> or starts with an identifier character:
    19         * - for an '-'. This will be the only character
    20         * : for an key. The value is the next token
    21         * | for "OR"
    22         * ' ' for anything else.
    23         * @return The next token in the stream.
    24         */
    25         public String nextToken() {
    26                 if (!pushBackBuf.isEmpty()) {
    27                         return pushBackBuf.removeLast();
    28                 }
     17    /**
     18    * The token returned is <code>null</code> or starts with an identifier character:
     19    * - for an '-'. This will be the only character
     20    * : for an key. The value is the next token
     21    * | for "OR"
     22    * ' ' for anything else.
     23    * @return The next token in the stream.
     24    */
     25    public String nextToken() {
     26        if (!pushBackBuf.isEmpty()) {
     27            return pushBackBuf.removeLast();
     28        }
    2929
    30                 try {
    31                         int next;
    32                         char c = ' ';
    33                         while (c == ' ' || c == '\t' || c == '\n') {
    34                                 next = search.read();
    35                                 if (next == -1)
    36                                         return null;
    37                                 c = (char)next;
    38                         }
    39                         StringBuilder s;
    40                         switch (c) {
    41                         case ':':
    42                                 next = search.read();
    43                                 c = (char) next;
    44                                 if (next == -1 || c == ' ' || c == '\t') {
    45                                         pushBack(" ");
    46                                 } else {
    47                                         search.unread(next);
    48                                 }
    49                                 return ":";
    50                         case '-':
    51                                 return "-";
    52                         case '(':
    53                                 return "(";
    54                         case ')':
    55                                 return ")";
    56                         case '|':
    57                                 return "|";
    58                         case '"':
    59                                 s = new StringBuilder(" ");
    60                                 for (int nc = search.read(); nc != -1 && nc != '"'; nc = search.read())
    61                                         s.append((char)nc);
    62                                 return s.toString();
    63                         default:
    64                                 s = new StringBuilder();
    65                         for (;;) {
    66                                 s.append(c);
    67                                 next = search.read();
    68                                 if (next == -1) {
    69                                         if (s.toString().equals("OR"))
    70                                                 return "|";
    71                                         return " "+s.toString();
    72                                 }
    73                                 c = (char)next;
    74                                 if (c == ' ' || c == '\t' || c == '"' || c == ':' || c == '(' || c == ')' || c == '|') {
    75                                         search.unread(next);
    76                                         if (s.toString().equals("OR"))
    77                                                 return "|";
    78                                         return " "+s.toString();
    79                                 }
    80                         }
    81                         }
    82                 } catch (IOException e) {
    83                         throw new RuntimeException(e.getMessage(), e);
    84                 }               
    85         }
     30        try {
     31            int next;
     32            char c = ' ';
     33            while (c == ' ' || c == '\t' || c == '\n') {
     34                next = search.read();
     35                if (next == -1)
     36                    return null;
     37                c = (char)next;
     38            }
     39            StringBuilder s;
     40            switch (c) {
     41            case ':':
     42                next = search.read();
     43                c = (char) next;
     44                if (next == -1 || c == ' ' || c == '\t') {
     45                    pushBack(" ");
     46                } else {
     47                    search.unread(next);
     48                }
     49                return ":";
     50            case '-':
     51                return "-";
     52            case '(':
     53                return "(";
     54            case ')':
     55                return ")";
     56            case '|':
     57                return "|";
     58            case '"':
     59                s = new StringBuilder(" ");
     60                for (int nc = search.read(); nc != -1 && nc != '"'; nc = search.read())
     61                    s.append((char)nc);
     62                return s.toString();
     63            default:
     64                s = new StringBuilder();
     65            for (;;) {
     66                s.append(c);
     67                next = search.read();
     68                if (next == -1) {
     69                    if (s.toString().equals("OR"))
     70                        return "|";
     71                    return " "+s.toString();
     72                }
     73                c = (char)next;
     74                if (c == ' ' || c == '\t' || c == '"' || c == ':' || c == '(' || c == ')' || c == '|') {
     75                    search.unread(next);
     76                    if (s.toString().equals("OR"))
     77                        return "|";
     78                    return " "+s.toString();
     79                }
     80            }
     81            }
     82        } catch (IOException e) {
     83            throw new RuntimeException(e.getMessage(), e);
     84        }
     85    }
    8686
    87         public boolean readIfEqual(String tok) {
    88                 String nextTok = nextToken();
    89                 if (nextTok == null ? tok == null : nextTok.equals(tok))
    90                         return true;
    91                 pushBack(nextTok);
    92                 return false;
    93         }
     87    public boolean readIfEqual(String tok) {
     88        String nextTok = nextToken();
     89        if (nextTok == null ? tok == null : nextTok.equals(tok))
     90            return true;
     91        pushBack(nextTok);
     92        return false;
     93    }
    9494
    95         public String readText() {
    96                 String nextTok = nextToken();
    97                 if (nextTok != null && nextTok.startsWith(" "))
    98                         return nextTok.substring(1);
    99                 pushBack(nextTok);
    100                 return null;
    101         }
     95    public String readText() {
     96        String nextTok = nextToken();
     97        if (nextTok != null && nextTok.startsWith(" "))
     98            return nextTok.substring(1);
     99        pushBack(nextTok);
     100        return null;
     101    }
    102102
    103         public void pushBack(String tok) {
    104                 pushBackBuf.addLast(tok);
    105         }
     103    public void pushBack(String tok) {
     104        pushBackBuf.addLast(tok);
     105    }
    106106}
  • trunk/src/org/openstreetmap/josm/actions/search/SearchCompiler.java

    r1168 r1169  
    2020public class SearchCompiler {
    2121
    22         private boolean caseSensitive = false;
    23         private PushbackTokenizer tokenizer;
    24 
    25         public SearchCompiler(boolean caseSensitive, PushbackTokenizer tokenizer) {
    26                 this.caseSensitive = caseSensitive;
    27                 this.tokenizer = tokenizer;
    28         }
    29        
    30         abstract public static class Match {
    31                 abstract public boolean match(OsmPrimitive osm);
    32         }
    33 
    34         private static class Always extends Match {
    35                 @Override public boolean match(OsmPrimitive osm) {
    36                         return true;
    37                 }
    38         }
    39 
    40         private static class Not extends Match {
    41                 private final Match match;
    42                 public Not(Match match) {this.match = match;}
    43                 @Override public boolean match(OsmPrimitive osm) {
    44                         return !match.match(osm);
    45                 }
    46                 @Override public String toString() {return "!"+match;}
    47         }
    48 
    49         private static class And extends Match {
    50                 private Match lhs;
    51                 private Match rhs;
    52                 public And(Match lhs, Match rhs) {this.lhs = lhs; this.rhs = rhs;}
    53                 @Override public boolean match(OsmPrimitive osm) {
    54                         return lhs.match(osm) && rhs.match(osm);
    55                 }
    56                 @Override public String toString() {return lhs+" && "+rhs;}
    57         }
    58 
    59         private static class Or extends Match {
    60                 private Match lhs;
    61                 private Match rhs;
    62                 public Or(Match lhs, Match rhs) {this.lhs = lhs; this.rhs = rhs;}
    63                 @Override public boolean match(OsmPrimitive osm) {
    64                         return lhs.match(osm) || rhs.match(osm);
    65                 }
    66                 @Override public String toString() {return lhs+" || "+rhs;}
    67         }
    68 
    69         private static class Id extends Match {
    70                 private long id;
    71                 public Id(long id) {this.id = id;}
    72                 @Override public boolean match(OsmPrimitive osm) {
    73                         return osm.id == id;
    74                 }
    75                 @Override public String toString() {return "id="+id;}
    76         }
    77 
    78         private class KeyValue extends Match {
    79                 private String key;
    80                 private String value;
    81                 public KeyValue(String key, String value) {this.key = key; this.value = value; }
    82                 @Override public boolean match(OsmPrimitive osm) {
    83                         String value = null;
    84                         if (key.equals("timestamp"))
    85                                 value = osm.getTimeStr();
    86                         else
    87                                 value = osm.get(key);
    88                         if (value == null)
    89                                 return false;
    90                         String v1 = caseSensitive ? value : value.toLowerCase();
    91                         String v2 = caseSensitive ? this.value : this.value.toLowerCase();
    92                         // is not Java 1.5
    93                         //v1 = java.text.Normalizer.normalize(v1, java.text.Normalizer.Form.NFC);
    94                         //v2 = java.text.Normalizer.normalize(v2, java.text.Normalizer.Form.NFC);
    95                         return v1.indexOf(v2) != -1;
    96                 }
    97                 @Override public String toString() {return key+"="+value;}
    98         }
    99 
    100         private class Any extends Match {
    101                 private String s;
    102                 public Any(String s) {this.s = s;}
    103                 @Override public boolean match(OsmPrimitive osm) {
    104                         if (osm.keys == null)
    105                                 return s.equals("");
    106                         String search = caseSensitive ? s : s.toLowerCase();
    107                         // is not Java 1.5
    108                         //search = java.text.Normalizer.normalize(search, java.text.Normalizer.Form.NFC);
    109                         for (Entry<String, String> e : osm.keys.entrySet()) {
    110                                 String key = caseSensitive ? e.getKey() : e.getKey().toLowerCase();
    111                                 String value = caseSensitive ? e.getValue() : e.getValue().toLowerCase();
    112                                 // is not Java 1.5
    113                                 //value = java.text.Normalizer.normalize(value, java.text.Normalizer.Form.NFC);
    114                                 if (key.indexOf(search) != -1 || value.indexOf(search) != -1)
    115                                         return true;
    116                         }
    117                         if (osm.user != null) {
    118                                 String name = osm.user.name;
    119                                 // is not Java 1.5
    120                                 //String name = java.text.Normalizer.normalize(name, java.text.Normalizer.Form.NFC);
    121                                 if (!caseSensitive)
    122                                         name = name.toLowerCase();
    123                                 if (name.indexOf(search) != -1)
    124                                         return true;
    125                         }
    126                         return false;
    127                 }
    128                 @Override public String toString() {return s;}
    129         }
    130 
    131         private static class ExactType extends Match {
    132                 private String type;
    133                 public ExactType(String type) {this.type = type;}
    134                 @Override public boolean match(OsmPrimitive osm) {
    135                         if (osm instanceof Node)
    136                                 return type.equals("node");
    137                         if (osm instanceof Way)
    138                                 return type.equals("way");
    139                         if (osm instanceof Relation)
    140                                 return type.equals("relation");
    141                         throw new IllegalStateException("unknown class "+osm.getClass());
    142                 }
    143                 @Override public String toString() {return "type="+type;}
    144         }
    145 
    146         private static class UserMatch extends Match {
    147                 private User user;
    148                 public UserMatch(String user) { this.user = User.get(user); }
    149                 @Override public boolean match(OsmPrimitive osm) {
    150                         return osm.user == user;
    151                 }
    152                 @Override public String toString() { return "user=" + user.name; }
    153         }
    154 
    155         private static class NodeCount extends Match {
    156                 private int count;
    157                 public NodeCount(int count) {this.count = count;}
    158                 @Override public boolean match(OsmPrimitive osm) {
    159                         return osm instanceof Way && ((Way) osm).nodes.size() == count;
    160                 }
    161                 @Override public String toString() {return "nodes="+count;}
    162         }
    163 
    164         private static class Modified extends Match {
    165                 @Override public boolean match(OsmPrimitive osm) {
    166                         return osm.modified || osm.id == 0;
    167                 }
    168                 @Override public String toString() {return "modified";}
    169         }
    170        
    171         private static class Selected extends Match {
    172                 @Override public boolean match(OsmPrimitive osm) {
    173                         return osm.selected;
    174                 }
    175                 @Override public String toString() {return "selected";}
    176         }
    177 
    178         private static class Incomplete extends Match {
    179                 @Override public boolean match(OsmPrimitive osm) {
     22    private boolean caseSensitive = false;
     23    private PushbackTokenizer tokenizer;
     24
     25    public SearchCompiler(boolean caseSensitive, PushbackTokenizer tokenizer) {
     26        this.caseSensitive = caseSensitive;
     27        this.tokenizer = tokenizer;
     28    }
     29
     30    abstract public static class Match {
     31        abstract public boolean match(OsmPrimitive osm);
     32    }
     33
     34    private static class Always extends Match {
     35        @Override public boolean match(OsmPrimitive osm) {
     36            return true;
     37        }
     38    }
     39
     40    private static class Not extends Match {
     41        private final Match match;
     42        public Not(Match match) {this.match = match;}
     43        @Override public boolean match(OsmPrimitive osm) {
     44            return !match.match(osm);
     45        }
     46        @Override public String toString() {return "!"+match;}
     47    }
     48
     49    private static class And extends Match {
     50        private Match lhs;
     51        private Match rhs;
     52        public And(Match lhs, Match rhs) {this.lhs = lhs; this.rhs = rhs;}
     53        @Override public boolean match(OsmPrimitive osm) {
     54            return lhs.match(osm) && rhs.match(osm);
     55        }
     56        @Override public String toString() {return lhs+" && "+rhs;}
     57    }
     58
     59    private static class Or extends Match {
     60        private Match lhs;
     61        private Match rhs;
     62        public Or(Match lhs, Match rhs) {this.lhs = lhs; this.rhs = rhs;}
     63        @Override public boolean match(OsmPrimitive osm) {
     64            return lhs.match(osm) || rhs.match(osm);
     65        }
     66        @Override public String toString() {return lhs+" || "+rhs;}
     67    }
     68
     69    private static class Id extends Match {
     70        private long id;
     71        public Id(long id) {this.id = id;}
     72        @Override public boolean match(OsmPrimitive osm) {
     73            return osm.id == id;
     74        }
     75        @Override public String toString() {return "id="+id;}
     76    }
     77
     78    private class KeyValue extends Match {
     79        private String key;
     80        private String value;
     81        public KeyValue(String key, String value) {this.key = key; this.value = value; }
     82        @Override public boolean match(OsmPrimitive osm) {
     83            String value = null;
     84            if (key.equals("timestamp"))
     85                value = osm.getTimeStr();
     86            else
     87                value = osm.get(key);
     88            if (value == null)
     89                return false;
     90            String v1 = caseSensitive ? value : value.toLowerCase();
     91            String v2 = caseSensitive ? this.value : this.value.toLowerCase();
     92            // is not Java 1.5
     93            //v1 = java.text.Normalizer.normalize(v1, java.text.Normalizer.Form.NFC);
     94            //v2 = java.text.Normalizer.normalize(v2, java.text.Normalizer.Form.NFC);
     95            return v1.indexOf(v2) != -1;
     96        }
     97        @Override public String toString() {return key+"="+value;}
     98    }
     99
     100    private class Any extends Match {
     101        private String s;
     102        public Any(String s) {this.s = s;}
     103        @Override public boolean match(OsmPrimitive osm) {
     104            if (osm.keys == null)
     105                return s.equals("");
     106            String search = caseSensitive ? s : s.toLowerCase();
     107            // is not Java 1.5
     108            //search = java.text.Normalizer.normalize(search, java.text.Normalizer.Form.NFC);
     109            for (Entry<String, String> e : osm.keys.entrySet()) {
     110                String key = caseSensitive ? e.getKey() : e.getKey().toLowerCase();
     111                String value = caseSensitive ? e.getValue() : e.getValue().toLowerCase();
     112                // is not Java 1.5
     113                //value = java.text.Normalizer.normalize(value, java.text.Normalizer.Form.NFC);
     114                if (key.indexOf(search) != -1 || value.indexOf(search) != -1)
     115                    return true;
     116            }
     117            if (osm.user != null) {
     118                String name = osm.user.name;
     119                // is not Java 1.5
     120                //String name = java.text.Normalizer.normalize(name, java.text.Normalizer.Form.NFC);
     121                if (!caseSensitive)
     122                    name = name.toLowerCase();
     123                if (name.indexOf(search) != -1)
     124                    return true;
     125            }
     126            return false;
     127        }
     128        @Override public String toString() {return s;}
     129    }
     130
     131    private static class ExactType extends Match {
     132        private String type;
     133        public ExactType(String type) {this.type = type;}
     134        @Override public boolean match(OsmPrimitive osm) {
     135            if (osm instanceof Node)
     136                return type.equals("node");
     137            if (osm instanceof Way)
     138                return type.equals("way");
     139            if (osm instanceof Relation)
     140                return type.equals("relation");
     141            throw new IllegalStateException("unknown class "+osm.getClass());
     142        }
     143        @Override public String toString() {return "type="+type;}
     144    }
     145
     146    private static class UserMatch extends Match {
     147        private User user;
     148        public UserMatch(String user) { this.user = User.get(user); }
     149        @Override public boolean match(OsmPrimitive osm) {
     150            return osm.user == user;
     151        }
     152        @Override public String toString() { return "user=" + user.name; }
     153    }
     154
     155    private static class NodeCount extends Match {
     156        private int count;
     157        public NodeCount(int count) {this.count = count;}
     158        @Override public boolean match(OsmPrimitive osm) {
     159            return osm instanceof Way && ((Way) osm).nodes.size() == count;
     160        }
     161        @Override public String toString() {return "nodes="+count;}
     162    }
     163
     164    private static class Modified extends Match {
     165        @Override public boolean match(OsmPrimitive osm) {
     166            return osm.modified || osm.id == 0;
     167        }
     168        @Override public String toString() {return "modified";}
     169    }
     170
     171    private static class Selected extends Match {
     172        @Override public boolean match(OsmPrimitive osm) {
     173            return osm.selected;
     174        }
     175        @Override public String toString() {return "selected";}
     176    }
     177
     178    private static class Incomplete extends Match {
     179        @Override public boolean match(OsmPrimitive osm) {
    180180            return osm.incomplete;
    181                 }
    182                 @Override public String toString() {return "incomplete";}
    183         }
    184 
    185         public static class ParseError extends Exception {
    186                 public ParseError(String msg) {
    187                         super(msg);
    188                 }
    189         }
    190        
    191         public static Match compile(String searchStr, boolean caseSensitive)
    192                         throws ParseError {
    193                 return new SearchCompiler(caseSensitive,
    194                                 new PushbackTokenizer(
    195                                         new PushbackReader(new StringReader(searchStr))))
    196                         .parse();
    197         }
    198 
    199         public Match parse() throws ParseError {
    200                 Match m = parseJuxta();
    201                 if (!tokenizer.readIfEqual(null)) {
    202                         throw new ParseError("Unexpected token: " + tokenizer.nextToken());
    203                 }
    204                 return m;
    205         }
    206 
    207         private Match parseJuxta() throws ParseError {
    208                 Match juxta = new Always();
    209 
    210                 Match m;
    211                 while ((m = parseOr()) != null) {
    212                         juxta = new And(m, juxta);
    213                 }
    214 
    215                 return juxta;
    216         }
    217 
    218         private Match parseOr() throws ParseError {
    219                 Match a = parseNot();
    220                 if (tokenizer.readIfEqual("|")) {
    221                         Match b = parseNot();
    222                         if (a == null || b == null) {
    223                                 throw new ParseError(tr("Missing arguments for or."));
    224                         }
    225                         return new Or(a, b);
    226                 }
    227                 return a;
    228         }
    229 
    230         private Match parseNot() throws ParseError {
    231                 if (tokenizer.readIfEqual("-")) {
    232                         Match m = parseParens();
    233                         if (m == null) {
    234                                 throw new ParseError(tr("Missing argument for not."));
    235                         }
    236                         return new Not(m);
    237                 }
    238                 return parseParens();
    239         }
    240 
    241         private Match parseParens() throws ParseError {
    242                 if (tokenizer.readIfEqual("(")) {
    243                         Match m = parseJuxta();
    244                         if (!tokenizer.readIfEqual(")")) {
    245                                 throw new ParseError(tr("Expected closing parenthesis."));
    246                         }
    247                         return m;
    248                 }
    249                 return parsePat();
    250         }
    251 
    252         private Match parsePat() {
    253                 String tok = tokenizer.readText();
    254 
    255                 if (tokenizer.readIfEqual(":")) {
    256                         String tok2 = tokenizer.readText();
    257                         if (tok == null) tok = "";
    258                         if (tok2 == null) tok2 = "";
    259                         return parseKV(tok, tok2);
    260                 }
    261 
    262                 if (tok == null) {
    263                         return null;
    264                 } else if (tok.equals("modified")) {
    265                         return new Modified();
    266                 } else if (tok.equals("incomplete")) {
    267                         return new Incomplete();
    268                 } else if (tok.equals("selected")) {
    269                         return new Selected();
    270                 } else {
    271                         return new Any(tok);
    272                 }
    273         }
    274 
    275         private Match parseKV(String key, String value) {
    276                 if (key.equals("type")) {
    277                         return new ExactType(value);
    278                 } else if (key.equals("user")) {
    279                         return new UserMatch(value);
    280                 } else if (key.equals("nodes")) {
    281                         return new NodeCount(Integer.parseInt(value));
    282                 } else if (key.equals("id")) {
    283                         try {
    284                                 return new Id(Long.parseLong(value));
    285                         } catch (NumberFormatException x) {
    286                                 return new Id(0);
    287                         }
    288                 } else {
    289                         return new KeyValue(key, value);
    290                 }
    291         }
     181        }
     182        @Override public String toString() {return "incomplete";}
     183    }
     184
     185    public static class ParseError extends Exception {
     186        public ParseError(String msg) {
     187            super(msg);
     188        }
     189    }
     190
     191    public static Match compile(String searchStr, boolean caseSensitive)
     192            throws ParseError {
     193        return new SearchCompiler(caseSensitive,
     194                new PushbackTokenizer(
     195                    new PushbackReader(new StringReader(searchStr))))
     196            .parse();
     197    }
     198
     199    public Match parse() throws ParseError {
     200        Match m = parseJuxta();
     201        if (!tokenizer.readIfEqual(null)) {
     202            throw new ParseError("Unexpected token: " + tokenizer.nextToken());
     203        }
     204        return m;
     205    }
     206
     207    private Match parseJuxta() throws ParseError {
     208        Match juxta = new Always();
     209
     210        Match m;
     211        while ((m = parseOr()) != null) {
     212            juxta = new And(m, juxta);
     213        }
     214
     215        return juxta;
     216    }
     217
     218    private Match parseOr() throws ParseError {
     219        Match a = parseNot();
     220        if (tokenizer.readIfEqual("|")) {
     221            Match b = parseNot();
     222            if (a == null || b == null) {
     223                throw new ParseError(tr("Missing arguments for or."));
     224            }
     225            return new Or(a, b);
     226        }
     227        return a;
     228    }
     229
     230    private Match parseNot() throws ParseError {
     231        if (tokenizer.readIfEqual("-")) {
     232            Match m = parseParens();
     233            if (m == null) {
     234                throw new ParseError(tr("Missing argument for not."));
     235            }
     236            return new Not(m);
     237        }
     238        return parseParens();
     239    }
     240
     241    private Match parseParens() throws ParseError {
     242        if (tokenizer.readIfEqual("(")) {
     243            Match m = parseJuxta();
     244            if (!tokenizer.readIfEqual(")")) {
     245                throw new ParseError(tr("Expected closing parenthesis."));
     246            }
     247            return m;
     248        }
     249        return parsePat();
     250    }
     251
     252    private Match parsePat() {
     253        String tok = tokenizer.readText();
     254
     255        if (tokenizer.readIfEqual(":")) {
     256            String tok2 = tokenizer.readText();
     257            if (tok == null) tok = "";
     258            if (tok2 == null) tok2 = "";
     259            return parseKV(tok, tok2);
     260        }
     261
     262        if (tok == null) {
     263            return null;
     264        } else if (tok.equals("modified")) {
     265            return new Modified();
     266        } else if (tok.equals("incomplete")) {
     267            return new Incomplete();
     268        } else if (tok.equals("selected")) {
     269            return new Selected();
     270        } else {
     271            return new Any(tok);
     272        }
     273    }
     274
     275    private Match parseKV(String key, String value) {
     276        if (key.equals("type")) {
     277            return new ExactType(value);
     278        } else if (key.equals("user")) {
     279            return new UserMatch(value);
     280        } else if (key.equals("nodes")) {
     281            return new NodeCount(Integer.parseInt(value));
     282        } else if (key.equals("id")) {
     283            try {
     284                return new Id(Long.parseLong(value));
     285            } catch (NumberFormatException x) {
     286                return new Id(0);
     287            }
     288        } else {
     289            return new KeyValue(key, value);
     290        }
     291    }
    292292}
  • trunk/src/org/openstreetmap/josm/actions/search/SelectionWebsiteLoader.java

    r627 r1169  
    11// License: GPL. Copyright 2007 by Immanuel Scholz and others
    22/**
    3  * 
     3 *
    44 */
    55package org.openstreetmap.josm.actions.search;
     
    2626
    2727public class SelectionWebsiteLoader extends PleaseWaitRunnable {
    28         public final URL url;
    29         public Collection<OsmPrimitive> sel;
    30         private final SearchAction.SearchMode mode;
    31         private OsmIdReader idReader = new OsmIdReader();
    32         public SelectionWebsiteLoader(String urlStr, SearchAction.SearchMode mode) {
    33                 super(tr("Load Selection"));
    34                 this.mode = mode;
    35                 URL u = null;
    36                 try {u = new URL(urlStr);} catch (MalformedURLException e) {}
    37                 this.url = u;
    38         }
    39         @Override protected void realRun() {
    40                 Main.pleaseWaitDlg.currentAction.setText(tr("Contact {0}...", url.getHost()));
    41                 sel = mode != SearchAction.SearchMode.remove ? new LinkedList<OsmPrimitive>() : Main.ds.allNonDeletedPrimitives();
    42                 try {
    43                         URLConnection con = url.openConnection();
    44                         InputStream in = new ProgressInputStream(con, Main.pleaseWaitDlg);
    45                         Main.pleaseWaitDlg.currentAction.setText(tr("Downloading..."));
    46                         Map<Long, String> ids = idReader.parseIds(in);
    47                         for (OsmPrimitive osm : Main.ds.allNonDeletedPrimitives()) {
    48                                 if (ids.containsKey(osm.id) && osm.getClass().getName().toLowerCase().endsWith(ids.get(osm.id))) {
    49                                         if (mode == SearchAction.SearchMode.remove)
    50                                                 sel.remove(osm);
    51                                         else
    52                                                 sel.add(osm);
    53                                 }
    54                         }
    55                 } catch (IOException e) {
    56                         e.printStackTrace();
    57                         JOptionPane.showMessageDialog(Main.parent, tr("Could not read from url: \"{0}\"",url));
    58                 } catch (SAXException e) {
    59                         e.printStackTrace();
    60                         JOptionPane.showMessageDialog(Main.parent,tr("Parsing error in url: \"{0}\"",url));
    61                 }
    62         }
    63         @Override protected void cancel() {
    64                 sel = null;
    65                 idReader.cancel();
    66         }
    67         @Override protected void finish() {
    68                 if (sel != null)
    69                         Main.ds.setSelected(sel);
    70         }
     28    public final URL url;
     29    public Collection<OsmPrimitive> sel;
     30    private final SearchAction.SearchMode mode;
     31    private OsmIdReader idReader = new OsmIdReader();
     32    public SelectionWebsiteLoader(String urlStr, SearchAction.SearchMode mode) {
     33        super(tr("Load Selection"));
     34        this.mode = mode;
     35        URL u = null;
     36        try {u = new URL(urlStr);} catch (MalformedURLException e) {}
     37        this.url = u;
     38    }
     39    @Override protected void realRun() {
     40        Main.pleaseWaitDlg.currentAction.setText(tr("Contact {0}...", url.getHost()));
     41        sel = mode != SearchAction.SearchMode.remove ? new LinkedList<OsmPrimitive>() : Main.ds.allNonDeletedPrimitives();
     42        try {
     43            URLConnection con = url.openConnection();
     44            InputStream in = new ProgressInputStream(con, Main.pleaseWaitDlg);
     45            Main.pleaseWaitDlg.currentAction.setText(tr("Downloading..."));
     46            Map<Long, String> ids = idReader.parseIds(in);
     47            for (OsmPrimitive osm : Main.ds.allNonDeletedPrimitives()) {
     48                if (ids.containsKey(osm.id) && osm.getClass().getName().toLowerCase().endsWith(ids.get(osm.id))) {
     49                    if (mode == SearchAction.SearchMode.remove)
     50                        sel.remove(osm);
     51                    else
     52                        sel.add(osm);
     53                }
     54            }
     55        } catch (IOException e) {
     56            e.printStackTrace();
     57            JOptionPane.showMessageDialog(Main.parent, tr("Could not read from url: \"{0}\"",url));
     58        } catch (SAXException e) {
     59            e.printStackTrace();
     60            JOptionPane.showMessageDialog(Main.parent,tr("Parsing error in url: \"{0}\"",url));
     61        }
     62    }
     63    @Override protected void cancel() {
     64        sel = null;
     65        idReader.cancel();
     66    }
     67    @Override protected void finish() {
     68        if (sel != null)
     69            Main.ds.setSelected(sel);
     70    }
    7171}
  • trunk/src/org/openstreetmap/josm/command/AddCommand.java

    r655 r1169  
    2222 * A command that adds an osm primitive to a dataset. Keys cannot be added this
    2323 * way.
    24  * 
     24 *
    2525 * See {@link ChangeCommand ChangeCommand} for comments on relation back references.
    26  * 
     26 *
    2727 * @author imi
    2828 */
    2929public class AddCommand extends Command {
    3030
    31         /**
    32          * The primitive to add to the dataset.
    33          */
    34         private final OsmPrimitive osm;
    35        
    36         private DataSet ds;
     31    /**
     32     * The primitive to add to the dataset.
     33     */
     34    private final OsmPrimitive osm;
    3735
    38         /**
    39          * Create the command and specify the element to add.
    40          */
    41         public AddCommand(OsmPrimitive osm) {
    42                 this.osm = osm;
    43                 this.ds = Main.main.editLayer().data;
    44         }
     36    private DataSet ds;
    4537
    46         @Override public boolean executeCommand() {
    47                 osm.visit(new AddVisitor(ds));
    48                 return true;
    49         }
    50 
    51         @Override public void undoCommand() {
    52                 osm.visit(new DeleteVisitor(ds));
    53         }
    54 
    55         @Override public void fillModifiedData(Collection<OsmPrimitive> modified, Collection<OsmPrimitive> deleted, Collection<OsmPrimitive> added) {
    56                 added.add(osm);
    57         }
    58 
    59         // faster implementation
    60         @Override public boolean invalidBecauselayerRemoved(Layer oldLayer) {
    61             return oldLayer instanceof OsmDataLayer && ((OsmDataLayer)oldLayer).data == ds;
     38    /**
     39     * Create the command and specify the element to add.
     40     */
     41    public AddCommand(OsmPrimitive osm) {
     42        this.osm = osm;
     43        this.ds = Main.main.editLayer().data;
    6244    }
    6345
    64         @Override public MutableTreeNode description() {
    65                 NameVisitor v = new NameVisitor();
    66                 osm.visit(v);
    67                 return new DefaultMutableTreeNode(new JLabel(tr("Add")+" "+tr(v.className)+" "+v.name, v.icon, JLabel.HORIZONTAL));
     46    @Override public boolean executeCommand() {
     47        osm.visit(new AddVisitor(ds));
     48        return true;
     49    }
     50
     51    @Override public void undoCommand() {
     52        osm.visit(new DeleteVisitor(ds));
     53    }
     54
     55    @Override public void fillModifiedData(Collection<OsmPrimitive> modified, Collection<OsmPrimitive> deleted, Collection<OsmPrimitive> added) {
     56        added.add(osm);
     57    }
     58
     59    // faster implementation
     60    @Override public boolean invalidBecauselayerRemoved(Layer oldLayer) {
     61        return oldLayer instanceof OsmDataLayer && ((OsmDataLayer)oldLayer).data == ds;
     62    }
     63
     64    @Override public MutableTreeNode description() {
     65        NameVisitor v = new NameVisitor();
     66        osm.visit(v);
     67        return new DefaultMutableTreeNode(new JLabel(tr("Add")+" "+tr(v.className)+" "+v.name, v.icon, JLabel.HORIZONTAL));
    6868    }
    6969}
  • trunk/src/org/openstreetmap/josm/command/ChangeCommand.java

    r630 r1169  
    1414
    1515/**
    16  * Command that basically replaces one OSM primitive by another of the 
     16 * Command that basically replaces one OSM primitive by another of the
    1717 * same type.
    18  * 
     18 *
    1919 * @author Imi
    2020 */
    2121public class ChangeCommand extends Command {
    2222
    23         private final OsmPrimitive osm;
    24         private final OsmPrimitive newOsm;
     23    private final OsmPrimitive osm;
     24    private final OsmPrimitive newOsm;
    2525
    26         public ChangeCommand(OsmPrimitive osm, OsmPrimitive newOsm) {
    27                 this.osm = osm;
    28                 this.newOsm = newOsm;
     26    public ChangeCommand(OsmPrimitive osm, OsmPrimitive newOsm) {
     27        this.osm = osm;
     28        this.newOsm = newOsm;
    2929    }
    3030
    31         @Override public boolean executeCommand() {
    32             super.executeCommand();
    33             osm.cloneFrom(newOsm);
    34             osm.modified = true;
    35                 return true;
     31    @Override public boolean executeCommand() {
     32        super.executeCommand();
     33        osm.cloneFrom(newOsm);
     34        osm.modified = true;
     35        return true;
    3636    }
    3737
    38         @Override public void fillModifiedData(Collection<OsmPrimitive> modified, Collection<OsmPrimitive> deleted, Collection<OsmPrimitive> added) {
    39                 modified.add(osm);
     38    @Override public void fillModifiedData(Collection<OsmPrimitive> modified, Collection<OsmPrimitive> deleted, Collection<OsmPrimitive> added) {
     39        modified.add(osm);
    4040    }
    4141
    42         @Override public MutableTreeNode description() {
    43                 NameVisitor v = new NameVisitor();
    44                 osm.visit(v);
    45                 return new DefaultMutableTreeNode(new JLabel(tr("Change")+" "+tr(v.className)+" "+v.name, v.icon, JLabel.HORIZONTAL));
     42    @Override public MutableTreeNode description() {
     43        NameVisitor v = new NameVisitor();
     44        osm.visit(v);
     45        return new DefaultMutableTreeNode(new JLabel(tr("Change")+" "+tr(v.className)+" "+v.name, v.icon, JLabel.HORIZONTAL));
    4646    }
    4747}
  • trunk/src/org/openstreetmap/josm/command/ChangePropertyCommand.java

    r1101 r1169  
    2020 * Command that manipulate the key/value structure of several objects. Manages deletion,
    2121 * adding and modify of values and keys.
    22  * 
     22 *
    2323 * @author imi
    2424 */
    2525public class ChangePropertyCommand extends Command {
    26         /**
    27          * All primitives that are affected with this command.
    28          */
    29         private final List<OsmPrimitive> objects;
    30         /**
    31          * The key that is subject to change.
    32          */
    33         private final String key;
    34         /**
    35          * The key value. If it is <code>null</code>, delete all key references with the given
    36          * key. Otherwise, change the properties of all objects to the given value or create keys of
    37          * those objects that do not have the key yet.
    38          */
    39         private final String value;
    40        
    41         public ChangePropertyCommand(Collection<? extends OsmPrimitive> objects, String key, String value) {
    42                 this.objects = new LinkedList<OsmPrimitive>();
    43                 this.key = key;
    44                 this.value = value;
    45                 if (value == null) {
    46                         for (OsmPrimitive osm : objects) {
    47                                 if(osm.get(key) != null)
    48                                         this.objects.add(osm);
    49                         }
    50                 } else {
    51                         for (OsmPrimitive osm : objects) {
    52                                 String val = osm.get(key);
    53                                 if (val == null || !value.equals(val)) {
    54                                         this.objects.add(osm);
    55                                 }
    56                         }
    57                 }
    58         }
     26    /**
     27     * All primitives that are affected with this command.
     28     */
     29    private final List<OsmPrimitive> objects;
     30    /**
     31     * The key that is subject to change.
     32     */
     33    private final String key;
     34    /**
     35     * The key value. If it is <code>null</code>, delete all key references with the given
     36     * key. Otherwise, change the properties of all objects to the given value or create keys of
     37     * those objects that do not have the key yet.
     38     */
     39    private final String value;
    5940
    60         public ChangePropertyCommand(OsmPrimitive object, String key, String value) {
    61                 this.objects = new LinkedList<OsmPrimitive>();
    62                 this.key = key;
    63                 this.value = value;
    64                 String val = object.get(key);
    65                 if ((value == null && val != null)
    66                 || (value != null && (val == null || !value.equals(val))))
    67                         this.objects.add(object);
    68         }
    69        
    70         @Override public boolean executeCommand() {
    71                 super.executeCommand(); // save old
    72                 if (value == null) {
    73                         for (OsmPrimitive osm : objects) {
    74                                 osm.modified = true;
    75                                 osm.remove(key);
    76                         }
    77                 } else {
    78                         for (OsmPrimitive osm : objects) {
    79                                 osm.modified = true;
    80                                 osm.put(key, value);
    81                         }
    82                 }
    83                 return true;
    84         }
     41    public ChangePropertyCommand(Collection<? extends OsmPrimitive> objects, String key, String value) {
     42        this.objects = new LinkedList<OsmPrimitive>();
     43        this.key = key;
     44        this.value = value;
     45        if (value == null) {
     46            for (OsmPrimitive osm : objects) {
     47                if(osm.get(key) != null)
     48                    this.objects.add(osm);
     49            }
     50        } else {
     51            for (OsmPrimitive osm : objects) {
     52                String val = osm.get(key);
     53                if (val == null || !value.equals(val)) {
     54                    this.objects.add(osm);
     55                }
     56            }
     57        }
     58    }
    8559
    86         @Override public void fillModifiedData(Collection<OsmPrimitive> modified, Collection<OsmPrimitive> deleted, Collection<OsmPrimitive> added) {
    87                 modified.addAll(objects);
    88         }
     60    public ChangePropertyCommand(OsmPrimitive object, String key, String value) {
     61        this.objects = new LinkedList<OsmPrimitive>();
     62        this.key = key;
     63        this.value = value;
     64        String val = object.get(key);
     65        if ((value == null && val != null)
     66        || (value != null && (val == null || !value.equals(val))))
     67            this.objects.add(object);
     68    }
    8969
    90         @Override public MutableTreeNode description() {
    91                 String text = value == null ? tr( "Remove \"{0}\" for", key) : tr("Set {0}={1} for",key,value);
    92                 if (objects.size() == 1) {
    93                         NameVisitor v = new NameVisitor();
    94                         objects.iterator().next().visit(v);
    95                         text += " "+tr(v.className)+" "+v.name;
    96                 } else
    97                         text += " "+objects.size()+" "+trn("object","objects",objects.size());
    98                 DefaultMutableTreeNode root = new DefaultMutableTreeNode(new JLabel(text, ImageProvider.get("data", "key"), JLabel.HORIZONTAL));
    99                 if (objects.size() == 1)
    100                         return root;
    101                 NameVisitor v = new NameVisitor();
    102                 for (OsmPrimitive osm : objects) {
    103                         osm.visit(v);
    104                         root.add(new DefaultMutableTreeNode(v.toLabel()));
    105                 }
    106                 return root;
     70    @Override public boolean executeCommand() {
     71        super.executeCommand(); // save old
     72        if (value == null) {
     73            for (OsmPrimitive osm : objects) {
     74                osm.modified = true;
     75                osm.remove(key);
     76            }
     77        } else {
     78            for (OsmPrimitive osm : objects) {
     79                osm.modified = true;
     80                osm.put(key, value);
     81            }
     82        }
     83        return true;
     84    }
     85
     86    @Override public void fillModifiedData(Collection<OsmPrimitive> modified, Collection<OsmPrimitive> deleted, Collection<OsmPrimitive> added) {
     87        modified.addAll(objects);
     88    }
     89
     90    @Override public MutableTreeNode description() {
     91        String text = value == null ? tr( "Remove \"{0}\" for", key) : tr("Set {0}={1} for",key,value);
     92        if (objects.size() == 1) {
     93            NameVisitor v = new NameVisitor();
     94            objects.iterator().next().visit(v);
     95            text += " "+tr(v.className)+" "+v.name;
     96        } else
     97            text += " "+objects.size()+" "+trn("object","objects",objects.size());
     98        DefaultMutableTreeNode root = new DefaultMutableTreeNode(new JLabel(text, ImageProvider.get("data", "key"), JLabel.HORIZONTAL));
     99        if (objects.size() == 1)
     100            return root;
     101        NameVisitor v = new NameVisitor();
     102        for (OsmPrimitive osm : objects) {
     103            osm.visit(v);
     104            root.add(new DefaultMutableTreeNode(v.toLabel()));
     105        }
     106        return root;
    107107    }
    108108}
  • trunk/src/org/openstreetmap/josm/command/Command.java

    r655 r1169  
    2525 * one atomic action on a specific dataset, such as move or delete.
    2626 *
    27  * Remember that the command must be executable and undoable, even if the 
     27 * Remember that the command must be executable and undoable, even if the
    2828 * Main.ds has changed, so the command must save the dataset it operates on
    2929 * if necessary.
     
    4747   }
    4848
    49    private CloneVisitor orig; 
     49   private CloneVisitor orig;
    5050
    5151   protected DataSet ds;
     
    7070
    7171   /**
    72     * Undoes the command. 
     72    * Undoes the command.
    7373    * It can be assumed that all objects are in the same state they were before.
    7474    * It can also be assumed that executeCommand was called exactly once before.
    75     * 
     75    *
    7676    * This implementation undoes all objects stored by a former call to executeCommand.
    7777    */
     
    122122    * Fill in the changed data this command operates on.
    123123    * Add to the lists, don't clear them.
    124     * 
     124    *
    125125    * @param modified The modified primitives
    126126    * @param deleted The deleted primitives
  • trunk/src/org/openstreetmap/josm/command/ConflictResolveCommand.java

    r630 r1169  
    2525public class ConflictResolveCommand extends Command {
    2626
    27         private final Collection<ConflictItem> conflicts;
    28         private final Map<OsmPrimitive, OsmPrimitive> resolved;
    29         private Map<OsmPrimitive, OsmPrimitive> origAllConflicts;
    30         private final ConflictDialog conflictDialog;
     27    private final Collection<ConflictItem> conflicts;
     28    private final Map<OsmPrimitive, OsmPrimitive> resolved;
     29    private Map<OsmPrimitive, OsmPrimitive> origAllConflicts;
     30    private final ConflictDialog conflictDialog;
    3131
    32         public ConflictResolveCommand(List<ConflictItem> conflicts, Map<OsmPrimitive, OsmPrimitive> resolved) {
    33                 this.conflicts = conflicts;
    34                 this.resolved = resolved;
    35                 conflictDialog = Main.map.conflictDialog;
    36         }
     32    public ConflictResolveCommand(List<ConflictItem> conflicts, Map<OsmPrimitive, OsmPrimitive> resolved) {
     33        this.conflicts = conflicts;
     34        this.resolved = resolved;
     35        conflictDialog = Main.map.conflictDialog;
     36    }
    3737
    38         @Override public boolean executeCommand() {
    39                 super.executeCommand();
     38    @Override public boolean executeCommand() {
     39        super.executeCommand();
    4040
    41                 origAllConflicts = new HashMap<OsmPrimitive, OsmPrimitive>(conflictDialog.conflicts);
    42                
    43                 Set<OsmPrimitive> completed = new HashSet<OsmPrimitive>(resolved.keySet());
    44                 for (ConflictItem ci : conflicts) {
    45                         for (Entry<OsmPrimitive, OsmPrimitive> e : resolved.entrySet()) {
    46                                 if (ci.resolution == ConflictResolver.Resolution.THEIR)
    47                                         ci.apply(e.getKey(), e.getValue());
    48                                 else if (ci.resolution == ConflictResolver.Resolution.MY)
    49                                         ci.apply(e.getValue(), e.getKey());
    50                                 else if (ci.hasConflict(e.getKey(), e.getValue()))
    51                                         completed.remove(e.getKey());
    52                         }
    53                 }
    54                 if (!completed.isEmpty()) {
    55                         for (OsmPrimitive k : completed)
    56                                 conflictDialog.conflicts.remove(k);
    57                         conflictDialog.rebuildList();
    58                 }
    59                 return true;
    60         }
     41        origAllConflicts = new HashMap<OsmPrimitive, OsmPrimitive>(conflictDialog.conflicts);
    6142
    62         @Override public void undoCommand() {
    63                 super.undoCommand();
    64                 Main.map.conflictDialog.conflicts.clear();
    65                 Main.map.conflictDialog.conflicts.putAll(origAllConflicts);
    66                 Main.map.conflictDialog.rebuildList();
    67         }
     43        Set<OsmPrimitive> completed = new HashSet<OsmPrimitive>(resolved.keySet());
     44        for (ConflictItem ci : conflicts) {
     45            for (Entry<OsmPrimitive, OsmPrimitive> e : resolved.entrySet()) {
     46                if (ci.resolution == ConflictResolver.Resolution.THEIR)
     47                    ci.apply(e.getKey(), e.getValue());
     48                else if (ci.resolution == ConflictResolver.Resolution.MY)
     49                    ci.apply(e.getValue(), e.getKey());
     50                else if (ci.hasConflict(e.getKey(), e.getValue()))
     51                    completed.remove(e.getKey());
     52            }
     53        }
     54        if (!completed.isEmpty()) {
     55            for (OsmPrimitive k : completed)
     56                conflictDialog.conflicts.remove(k);
     57            conflictDialog.rebuildList();
     58        }
     59        return true;
     60    }
    6861
    69         @Override public void fillModifiedData(Collection<OsmPrimitive> modified, Collection<OsmPrimitive> deleted, Collection<OsmPrimitive> added) {
    70                 modified.addAll(resolved.keySet());
    71         }
     62    @Override public void undoCommand() {
     63        super.undoCommand();
     64        Main.map.conflictDialog.conflicts.clear();
     65        Main.map.conflictDialog.conflicts.putAll(origAllConflicts);
     66        Main.map.conflictDialog.rebuildList();
     67    }
    7268
    73         @Override public MutableTreeNode description() {
    74                 int i = 0;
    75                 for (ConflictItem c : conflicts)
    76                         if (c.resolution != null)
    77                                 i++;
    78                 return new DefaultMutableTreeNode(new JLabel(tr("Resolve {0} conflicts in {1} objects",i,resolved.size()), ImageProvider.get("data", "object"), JLabel.HORIZONTAL));
     69    @Override public void fillModifiedData(Collection<OsmPrimitive> modified, Collection<OsmPrimitive> deleted, Collection<OsmPrimitive> added) {
     70        modified.addAll(resolved.keySet());
     71    }
     72
     73    @Override public MutableTreeNode description() {
     74        int i = 0;
     75        for (ConflictItem c : conflicts)
     76            if (c.resolution != null)
     77                i++;
     78        return new DefaultMutableTreeNode(new JLabel(tr("Resolve {0} conflicts in {1} objects",i,resolved.size()), ImageProvider.get("data", "object"), JLabel.HORIZONTAL));
    7979    }
    8080}
  • trunk/src/org/openstreetmap/josm/command/DeleteCommand.java

    r1047 r1169  
    105105    /**
    106106     * Delete the primitives and everything they reference.
    107      * 
     107     *
    108108     * If a node is deleted, the node and all ways and relations the node is part of are deleted as
    109109     * well.
    110      * 
     110     *
    111111     * If a way is deleted, all relations the way is member of are also deleted.
    112      * 
     112     *
    113113     * If a way is deleted, only the way and no nodes are deleted.
    114      * 
     114     *
    115115     * @param selection The list of all object to be deleted.
    116116     * @return command A command to perform the deletions, or null of there is nothing to delete.
     
    130130    /**
    131131     * Try to delete all given primitives.
    132      * 
     132     *
    133133     * If a node is used by a way, it's removed from that way. If a node or a way is used by a
    134134     * relation, inform the user and do not delete.
    135      * 
     135     *
    136136     * If this would cause ways with less than 2 nodes to be created, delete these ways instead. If
    137137     * they are part of a relation, inform the user and do not delete.
    138      * 
     138     *
    139139     * @param selection The objects to delete.
    140140     * @param alsoDeleteNodesInWay <code>true</code> if nodes should be deleted as well
     
    341341                            // leave message in one tr() as there is a grammatical connection.
    342342                            tr("You are about to delete nodes outside of the area you have downloaded." +
    343                             "<br>" + 
     343                            "<br>" +
    344344                            "This can cause problems because other objects (that you don't see) might use them." +
    345                             "<br>" + 
     345                            "<br>" +
    346346                            "Do you really want to delete?") + "</html>"));
    347347                        return DontShowAgainInfo.show("delete_outside_nodes", msg, false, JOptionPane.YES_NO_OPTION, JOptionPane.YES_OPTION);
  • trunk/src/org/openstreetmap/josm/command/MoveCommand.java

    r655 r1169  
    2626 * MoveCommand moves a set of OsmPrimitives along the map. It can be moved again
    2727 * to collect several MoveCommands into one command.
    28  * 
     28 *
    2929 * @author imi
    3030 */
    3131public class MoveCommand extends Command {
    32         /**
    33         * The objects that should be moved.
    34         */
    35         public Collection<Node> objects = new LinkedList<Node>();
    36         /**
    37          * x difference movement. Coordinates are in northern/eastern
    38         */
    39         private double x;
    40         /**
    41          * y difference movement. Coordinates are in northern/eastern
    42         */
    43         private double y;
     32    /**
     33    * The objects that should be moved.
     34    */
     35    public Collection<Node> objects = new LinkedList<Node>();
     36    /**
     37     * x difference movement. Coordinates are in northern/eastern
     38    */
     39    private double x;
     40    /**
     41     * y difference movement. Coordinates are in northern/eastern
     42    */
     43    private double y;
    4444
    45         /**
    46          * Small helper for holding the interesting part of the old data state of the
    47          * objects.
    48          */
    49         public static class OldState {
    50                 LatLon latlon;
    51                 EastNorth eastNorth;
    52                 boolean modified;
    53         }
    54        
    55         /**
    56          * List of all old states of the objects.
    57          */
    58         private List<OldState> oldState = new LinkedList<OldState>();
     45    /**
     46     * Small helper for holding the interesting part of the old data state of the
     47     * objects.
     48     */
     49    public static class OldState {
     50        LatLon latlon;
     51        EastNorth eastNorth;
     52        boolean modified;
     53    }
    5954
    60        
    61         public MoveCommand(OsmPrimitive osm, double x, double y) {
    62                 this(Collections.singleton(osm), x, y);
    63         }
    64         /**
    65          * Create a MoveCommand and assign the initial object set and movement vector.
    66          */
    67         public MoveCommand(Collection<OsmPrimitive> objects, double x, double y) {
    68                 this.x = x;
    69                 this.y = y;
    70                 this.objects = AllNodesVisitor.getAllNodes(objects);
    71                 for (Node n : this.objects) {
    72                         OldState os = new OldState();
    73                         os.eastNorth = n.eastNorth;
    74                         os.latlon = n.coor;
    75                         os.modified = n.modified;
    76                         oldState.add(os);
    77                 }
    78         }
     55    /**
     56     * List of all old states of the objects.
     57     */
     58    private List<OldState> oldState = new LinkedList<OldState>();
    7959
    80         /**
    81          * Move the same set of objects again by the specified vector. The vectors
    82          * are added together and so the resulting will be moved to the previous
    83          * vector plus this one.
    84          *
    85          * The move is immediately executed and any undo will undo both vectors to
    86          * the original position the objects had before first moving.
    87          */
    88         public void moveAgain(double x, double y) {
    89                 for (Node n : objects) {
    90                         n.eastNorth = new EastNorth(n.eastNorth.east()+x, n.eastNorth.north()+y);
    91                         n.coor = Main.proj.eastNorth2latlon(n.eastNorth);
    92                 }
    93                 this.x += x;
    94                 this.y += y;
    95         }
    96        
    97         @Override public boolean executeCommand() {
    98                 for (Node n : objects) {
    99                         n.eastNorth = new EastNorth(n.eastNorth.east()+x, n.eastNorth.north()+y);
    100                         n.coor = Main.proj.eastNorth2latlon(n.eastNorth);
    101                         n.modified = true;
    102                 }
    103                 return true;
    104         }
    10560
    106         @Override public void undoCommand() {
    107                 Iterator<OldState> it = oldState.iterator();
    108                 for (Node n : objects) {
    109                         OldState os = it.next();
    110                         n.eastNorth = os.eastNorth;
    111                         n.coor = os.latlon;
    112                         n.modified = os.modified;
    113                 }
    114         }
     61    public MoveCommand(OsmPrimitive osm, double x, double y) {
     62        this(Collections.singleton(osm), x, y);
     63    }
     64    /**
     65     * Create a MoveCommand and assign the initial object set and movement vector.
     66     */
     67    public MoveCommand(Collection<OsmPrimitive> objects, double x, double y) {
     68        this.x = x;
     69        this.y = y;
     70        this.objects = AllNodesVisitor.getAllNodes(objects);
     71        for (Node n : this.objects) {
     72            OldState os = new OldState();
     73            os.eastNorth = n.eastNorth;
     74            os.latlon = n.coor;
     75            os.modified = n.modified;
     76            oldState.add(os);
     77        }
     78    }
    11579
    116         @Override public void fillModifiedData(Collection<OsmPrimitive> modified, Collection<OsmPrimitive> deleted, Collection<OsmPrimitive> added) {
    117                 for (OsmPrimitive osm : objects)
    118                         modified.add(osm);
    119         }
     80    /**
     81     * Move the same set of objects again by the specified vector. The vectors
     82     * are added together and so the resulting will be moved to the previous
     83     * vector plus this one.
     84     *
     85     * The move is immediately executed and any undo will undo both vectors to
     86     * the original position the objects had before first moving.
     87     */
     88    public void moveAgain(double x, double y) {
     89        for (Node n : objects) {
     90            n.eastNorth = new EastNorth(n.eastNorth.east()+x, n.eastNorth.north()+y);
     91            n.coor = Main.proj.eastNorth2latlon(n.eastNorth);
     92        }
     93        this.x += x;
     94        this.y += y;
     95    }
    12096
    121         @Override public MutableTreeNode description() {
    122                 return new DefaultMutableTreeNode(new JLabel(tr("Move")+" "+objects.size()+" "+trn("node","nodes",objects.size()), ImageProvider.get("data", "node"), JLabel.HORIZONTAL));
     97    @Override public boolean executeCommand() {
     98        for (Node n : objects) {
     99            n.eastNorth = new EastNorth(n.eastNorth.east()+x, n.eastNorth.north()+y);
     100            n.coor = Main.proj.eastNorth2latlon(n.eastNorth);
     101            n.modified = true;
     102        }
     103        return true;
     104    }
     105
     106    @Override public void undoCommand() {
     107        Iterator<OldState> it = oldState.iterator();
     108        for (Node n : objects) {
     109            OldState os = it.next();
     110            n.eastNorth = os.eastNorth;
     111            n.coor = os.latlon;
     112            n.modified = os.modified;
     113        }
     114    }
     115
     116    @Override public void fillModifiedData(Collection<OsmPrimitive> modified, Collection<OsmPrimitive> deleted, Collection<OsmPrimitive> added) {
     117        for (OsmPrimitive osm : objects)
     118            modified.add(osm);
     119    }
     120
     121    @Override public MutableTreeNode description() {
     122        return new DefaultMutableTreeNode(new JLabel(tr("Move")+" "+objects.size()+" "+trn("node","nodes",objects.size()), ImageProvider.get("data", "node"), JLabel.HORIZONTAL));
    123123    }
    124124}
  • trunk/src/org/openstreetmap/josm/command/RotateCommand.java

    r748 r1169  
    2323/**
    2424 * RotateCommand rotates a number of objects around their centre.
    25  * 
     25 *
    2626 * @author Frederik Ramm <frederik@remote.org>
    2727 */
    2828public class RotateCommand extends Command {
    29        
    30         /**
    31          * The objects to rotate.
    32          */
    33         public Collection<Node> objects = new LinkedList<Node>();
    34        
    35         /**
    36          * pivot point
    37          */
    38         private Node pivot;
    39        
    40         /**
    41          * angle of rotation starting click to pivot
    42          */
    43         private double startAngle;
    44        
    45         /**
    46          * computed rotation angle between starting click and current mouse pos
    47          */
    48         private double rotationAngle;
    49        
    50         /**
    51          * List of all old states of the objects.
    52          */
    53         private Map<Node, MoveCommand.OldState> oldState = new HashMap<Node, MoveCommand.OldState>();
    54        
    55         /**
    56          * Creates a RotateCommand.
    57          * Assign the initial object set, compute pivot point and rotation angle.
    58          * Computation of pivot point is done by the same rules that are used in
    59          * the "align nodes in circle" action.
    60          */
    61         public RotateCommand(Collection<OsmPrimitive> objects, EastNorth start, EastNorth end) {
    6229
    63                 this.objects = AllNodesVisitor.getAllNodes(objects);
    64                 pivot = new Node(new LatLon(0,0));
    65                 pivot.eastNorth = new EastNorth(0,0);
     30    /**
     31     * The objects to rotate.
     32     */
     33    public Collection<Node> objects = new LinkedList<Node>();
    6634
    67                 for (Node n : this.objects) {
    68                         MoveCommand.OldState os = new MoveCommand.OldState();
    69                         os.eastNorth = n.eastNorth;
    70                         os.latlon = n.coor;
    71                         os.modified = n.modified;
    72                         oldState.put(n, os);
    73                         pivot.eastNorth = new EastNorth(pivot.eastNorth.east()+os.eastNorth.east(), pivot.eastNorth.north()+os.eastNorth.north());
    74                 }
    75                 pivot.eastNorth = new EastNorth(pivot.eastNorth.east()/this.objects.size(), pivot.eastNorth.north()/this.objects.size());
    76                 pivot.coor = Main.proj.eastNorth2latlon(pivot.eastNorth);
     35    /**
     36     * pivot point
     37     */
     38    private Node pivot;
    7739
    78                 rotationAngle = Math.PI/2;
    79                 rotateAgain(start, end);
    80         }
     40    /**
     41     * angle of rotation starting click to pivot
     42     */
     43    private double startAngle;
    8144
    82         /**
    83          * Rotate the same set of objects again, by the angle between given
    84          * start and end nodes. Internally this is added to the existing
    85          * rotation so a later undo will undo the whole rotation.
    86          */
    87         public void rotateAgain(EastNorth start, EastNorth end) {
    88                 // compute angle
    89                 startAngle = Math.atan2(start.east()-pivot.eastNorth.east(), start.north()-pivot.eastNorth.north());
    90                 double endAngle = Math.atan2(end.east()-pivot.eastNorth.east(), end.north()-pivot.eastNorth.north());
    91                 rotationAngle += startAngle - endAngle;
    92                 rotateNodes(false);
    93         }
     45    /**
     46     * computed rotation angle between starting click and current mouse pos
     47     */
     48    private double rotationAngle;
    9449
    95         /**
    96          * Helper for actually rotationg the nodes.
    97          * @param setModified - true if rotated nodes should be flagged "modified"
    98          */
    99         private void rotateNodes(boolean setModified) {
    100                 for (Node n : objects) {
    101                         double cosPhi = Math.cos(rotationAngle);
    102                         double sinPhi = Math.sin(rotationAngle);
    103                         EastNorth oldEastNorth = oldState.get(n).eastNorth;
    104                         double x = oldEastNorth.east() - pivot.eastNorth.east();
    105                         double y = oldEastNorth.north() - pivot.eastNorth.north();
    106                         double nx =  sinPhi * x + cosPhi * y + pivot.eastNorth.east();
    107                         double ny = -cosPhi * x + sinPhi * y + pivot.eastNorth.north();
    108                         n.eastNorth = new EastNorth(nx, ny);
    109                         n.coor = Main.proj.eastNorth2latlon(n.eastNorth);
    110                         if (setModified)
    111                                 n.modified = true;     
    112                 }
    113         }
    114        
    115         @Override public boolean executeCommand() {
    116                 rotateNodes(true);
    117                 return true;
    118         }
     50    /**
     51     * List of all old states of the objects.
     52     */
     53    private Map<Node, MoveCommand.OldState> oldState = new HashMap<Node, MoveCommand.OldState>();
    11954
    120         @Override public void undoCommand() {
    121                 for (Node n : objects) {
    122                         MoveCommand.OldState os = oldState.get(n);
    123                         n.eastNorth = os.eastNorth;
    124                         n.coor = os.latlon;
    125                         n.modified = os.modified;
    126                 }
    127         }
     55    /**
     56     * Creates a RotateCommand.
     57     * Assign the initial object set, compute pivot point and rotation angle.
     58     * Computation of pivot point is done by the same rules that are used in
     59     * the "align nodes in circle" action.
     60     */
     61    public RotateCommand(Collection<OsmPrimitive> objects, EastNorth start, EastNorth end) {
    12862
    129         @Override public void fillModifiedData(Collection<OsmPrimitive> modified, Collection<OsmPrimitive> deleted, Collection<OsmPrimitive> added) {
    130                 for (OsmPrimitive osm : objects)
    131                         modified.add(osm);
    132         }
     63        this.objects = AllNodesVisitor.getAllNodes(objects);
     64        pivot = new Node(new LatLon(0,0));
     65        pivot.eastNorth = new EastNorth(0,0);
    13366
    134         @Override public MutableTreeNode description() {
    135                 return new DefaultMutableTreeNode(new JLabel(tr("Rotate")+" "+objects.size()+" "+trn("node","nodes",objects.size()), ImageProvider.get("data", "node"), JLabel.HORIZONTAL));
     67        for (Node n : this.objects) {
     68            MoveCommand.OldState os = new MoveCommand.OldState();
     69            os.eastNorth = n.eastNorth;
     70            os.latlon = n.coor;
     71            os.modified = n.modified;
     72            oldState.put(n, os);
     73            pivot.eastNorth = new EastNorth(pivot.eastNorth.east()+os.eastNorth.east(), pivot.eastNorth.north()+os.eastNorth.north());
     74        }
     75        pivot.eastNorth = new EastNorth(pivot.eastNorth.east()/this.objects.size(), pivot.eastNorth.north()/this.objects.size());
     76        pivot.coor = Main.proj.eastNorth2latlon(pivot.eastNorth);
     77
     78        rotationAngle = Math.PI/2;
     79        rotateAgain(start, end);
     80    }
     81
     82    /**
     83     * Rotate the same set of objects again, by the angle between given
     84     * start and end nodes. Internally this is added to the existing
     85     * rotation so a later undo will undo the whole rotation.
     86     */
     87    public void rotateAgain(EastNorth start, EastNorth end) {
     88        // compute angle
     89        startAngle = Math.atan2(start.east()-pivot.eastNorth.east(), start.north()-pivot.eastNorth.north());
     90        double endAngle = Math.atan2(end.east()-pivot.eastNorth.east(), end.north()-pivot.eastNorth.north());
     91        rotationAngle += startAngle - endAngle;
     92        rotateNodes(false);
     93    }
     94
     95    /**
     96     * Helper for actually rotationg the nodes.
     97     * @param setModified - true if rotated nodes should be flagged "modified"
     98     */
     99    private void rotateNodes(boolean setModified) {
     100        for (Node n : objects) {
     101            double cosPhi = Math.cos(rotationAngle);
     102            double sinPhi = Math.sin(rotationAngle);
     103            EastNorth oldEastNorth = oldState.get(n).eastNorth;
     104            double x = oldEastNorth.east() - pivot.eastNorth.east();
     105            double y = oldEastNorth.north() - pivot.eastNorth.north();
     106            double nx =  sinPhi * x + cosPhi * y + pivot.eastNorth.east();
     107            double ny = -cosPhi * x + sinPhi * y + pivot.eastNorth.north();
     108            n.eastNorth = new EastNorth(nx, ny);
     109            n.coor = Main.proj.eastNorth2latlon(n.eastNorth);
     110            if (setModified)
     111                n.modified = true;
     112        }
     113    }
     114
     115    @Override public boolean executeCommand() {
     116        rotateNodes(true);
     117        return true;
     118    }
     119
     120    @Override public void undoCommand() {
     121        for (Node n : objects) {
     122            MoveCommand.OldState os = oldState.get(n);
     123            n.eastNorth = os.eastNorth;
     124            n.coor = os.latlon;
     125            n.modified = os.modified;
     126        }
     127    }
     128
     129    @Override public void fillModifiedData(Collection<OsmPrimitive> modified, Collection<OsmPrimitive> deleted, Collection<OsmPrimitive> added) {
     130        for (OsmPrimitive osm : objects)
     131            modified.add(osm);
     132    }
     133
     134    @Override public MutableTreeNode description() {
     135        return new DefaultMutableTreeNode(new JLabel(tr("Rotate")+" "+objects.size()+" "+trn("node","nodes",objects.size()), ImageProvider.get("data", "node"), JLabel.HORIZONTAL));
    136136    }
    137137}
  • trunk/src/org/openstreetmap/josm/command/SequenceCommand.java

    r853 r1169  
    2020public class SequenceCommand extends Command {
    2121
    22         /**
    23         * The command sequenz to be executed.
    24         */
    25         private Command[] sequence;
    26         private boolean sequence_complete;
    27         private final String name;
    28         public boolean continueOnError = false;
     22    /**
     23    * The command sequenz to be executed.
     24    */
     25    private Command[] sequence;
     26    private boolean sequence_complete;
     27    private final String name;
     28    public boolean continueOnError = false;
    2929
    30         /**
    31         * Create the command by specifying the list of commands to execute.
    32         * @param sequenz The sequence that should be executed.
    33         */
    34         public SequenceCommand(String name, Collection<Command> sequenz) {
    35                 this.name = name;
    36                 this.sequence = new Command[sequenz.size()];
    37                 this.sequence = sequenz.toArray(this.sequence);
    38         }
     30    /**
     31    * Create the command by specifying the list of commands to execute.
     32    * @param sequenz The sequence that should be executed.
     33    */
     34    public SequenceCommand(String name, Collection<Command> sequenz) {
     35        this.name = name;
     36        this.sequence = new Command[sequenz.size()];
     37        this.sequence = sequenz.toArray(this.sequence);
     38    }
    3939
    40         /**
    41          * Convenient constructor, if the commands are known at compile time.
    42          */
    43         public SequenceCommand(String name, Command... sequenz) {
    44                 this(name, Arrays.asList(sequenz));
    45         }
    46        
    47         public int executed_commands = 0;
    48         @Override public boolean executeCommand() {
    49                 for (int i=0; i < sequence.length; i++) {
    50                         Command c = sequence[i];
    51                         boolean result = c.executeCommand();
    52                         if (!result)
    53                                 Main.debug("SequenceCommand, executing command[" + i + "] " +  c + " result: " + result);
    54                         if (!result && !continueOnError) {
    55                                 this.undoCommands(i-1);
    56                                 return false;
    57                         }
    58                 }
    59                 sequence_complete = true;
    60                 return true;
    61         }
     40    /**
     41     * Convenient constructor, if the commands are known at compile time.
     42     */
     43    public SequenceCommand(String name, Command... sequenz) {
     44        this(name, Arrays.asList(sequenz));
     45    }
    6246
    63         public Command getLastCommand() {
    64                 if(sequence.length == 0)
    65                         return null;
    66                 return sequence[sequence.length-1];
    67         }
    68         private void undoCommands(int start) {
    69                 // We probably aborted this halfway though the
    70                 // execution sequence because of a sub-command
    71                 // error.  We already undid the sub-commands.
    72                 if (!sequence_complete)
    73                         return;
    74                 for (int i = start; i >= 0; --i)
    75                         sequence[i].undoCommand();
    76         }
     47    public int executed_commands = 0;
     48    @Override public boolean executeCommand() {
     49        for (int i=0; i < sequence.length; i++) {
     50            Command c = sequence[i];
     51            boolean result = c.executeCommand();
     52            if (!result)
     53                Main.debug("SequenceCommand, executing command[" + i + "] " +  c + " result: " + result);
     54            if (!result && !continueOnError) {
     55                this.undoCommands(i-1);
     56                return false;
     57            }
     58        }
     59        sequence_complete = true;
     60        return true;
     61    }
    7762
    78         @Override public void undoCommand() {
    79                 this.undoCommands(sequence.length-1);
    80         }
     63    public Command getLastCommand() {
     64        if(sequence.length == 0)
     65            return null;
     66        return sequence[sequence.length-1];
     67    }
     68    private void undoCommands(int start) {
     69        // We probably aborted this halfway though the
     70        // execution sequence because of a sub-command
     71        // error.  We already undid the sub-commands.
     72        if (!sequence_complete)
     73            return;
     74        for (int i = start; i >= 0; --i)
     75            sequence[i].undoCommand();
     76    }
    8177
    82         @Override public void fillModifiedData(Collection<OsmPrimitive> modified, Collection<OsmPrimitive> deleted, Collection<OsmPrimitive> added) {
    83                 for (Command c : sequence)
    84                         c.fillModifiedData(modified, deleted, added);
    85         }
     78    @Override public void undoCommand() {
     79        this.undoCommands(sequence.length-1);
     80    }
    8681
    87         @Override public MutableTreeNode description() {
    88                 DefaultMutableTreeNode root = new DefaultMutableTreeNode(tr("Sequence")+": "+name);
    89                 for (Command c : sequence)
    90                         root.add(c.description());
    91             return root;
     82    @Override public void fillModifiedData(Collection<OsmPrimitive> modified, Collection<OsmPrimitive> deleted, Collection<OsmPrimitive> added) {
     83        for (Command c : sequence)
     84            c.fillModifiedData(modified, deleted, added);
     85    }
     86
     87    @Override public MutableTreeNode description() {
     88        DefaultMutableTreeNode root = new DefaultMutableTreeNode(tr("Sequence")+": "+name);
     89        for (Command c : sequence)
     90            root.add(c.description());
     91        return root;
    9292    }
    9393}
  • trunk/src/org/openstreetmap/josm/corrector/CorrectionTable.java

    r1001 r1169  
    1313        extends JTable {
    1414
    15         private static final int MAX_VISIBLE_LINES = 10;
     15    private static final int MAX_VISIBLE_LINES = 10;
    1616
    1717    public static class BoldRenderer extends JLabel implements
    18                 TableCellRenderer {
     18            TableCellRenderer {
    1919
    20                 public Component getTableCellRendererComponent(JTable table,
    21                         Object value, boolean isSelected, boolean hasFocus, int row,
    22                         int column) {
     20        public Component getTableCellRendererComponent(JTable table,
     21                Object value, boolean isSelected, boolean hasFocus, int row,
     22                int column) {
    2323
    24                         Font f = getFont();
    25                         setFont(new Font(f.getName(), f.getStyle() | Font.BOLD, f.getSize()));
     24            Font f = getFont();
     25            setFont(new Font(f.getName(), f.getStyle() | Font.BOLD, f.getSize()));
    2626
    27                         setText((String)value);
     27            setText((String)value);
    2828
    29                         return this;
    30                 }
    31         }
     29            return this;
     30        }
     31    }
    3232
    33         private static BoldRenderer boldRenderer = null;
     33    private static BoldRenderer boldRenderer = null;
    3434
    35         protected CorrectionTable(TM correctionTableModel) {
    36                 super(correctionTableModel);
     35    protected CorrectionTable(TM correctionTableModel) {
     36        super(correctionTableModel);
    3737
    38                 final int correctionsSize = correctionTableModel.getCorrections().size();
    39                 final int lines = correctionsSize > MAX_VISIBLE_LINES ? MAX_VISIBLE_LINES
     38        final int correctionsSize = correctionTableModel.getCorrections().size();
     39        final int lines = correctionsSize > MAX_VISIBLE_LINES ? MAX_VISIBLE_LINES
    4040                : correctionsSize;
    41                 setPreferredScrollableViewportSize(new Dimension(400, lines
    42                         * getRowHeight()));
    43                 getColumnModel().getColumn(correctionTableModel.getApplyColumn())
     41        setPreferredScrollableViewportSize(new Dimension(400, lines
     42                * getRowHeight()));
     43        getColumnModel().getColumn(correctionTableModel.getApplyColumn())
    4444                .setPreferredWidth(40);
    45                 setRowSelectionAllowed(false);
    46         }
     45        setRowSelectionAllowed(false);
     46    }
    4747
    48         public TableCellRenderer getCellRenderer(int row, int column) {
    49                 if (getCorrectionTableModel().isBoldCell(row, column)) {
    50                         if (boldRenderer == null)
    51                                 boldRenderer = new BoldRenderer();
    52                         return boldRenderer;
    53                 }
    54                 return super.getCellRenderer(row, column);
    55         }
     48    public TableCellRenderer getCellRenderer(int row, int column) {
     49        if (getCorrectionTableModel().isBoldCell(row, column)) {
     50            if (boldRenderer == null)
     51                boldRenderer = new BoldRenderer();
     52            return boldRenderer;
     53        }
     54        return super.getCellRenderer(row, column);
     55    }
    5656
    57         @SuppressWarnings("unchecked")
     57    @SuppressWarnings("unchecked")
    5858    public TM getCorrectionTableModel() {
    59                 return (TM)getModel();
    60         }
     59        return (TM)getModel();
     60    }
    6161
    6262}
  • trunk/src/org/openstreetmap/josm/corrector/CorrectionTableModel.java

    r1001 r1169  
    1212        AbstractTableModel {
    1313
    14         private List<C> corrections;
    15         private boolean[] apply;
    16         private int applyColumn;
     14    private List<C> corrections;
     15    private boolean[] apply;
     16    private int applyColumn;
    1717
    18         public CorrectionTableModel(List<C> corrections) {
    19                 super();
    20                 this.corrections = corrections;
    21                 apply = new boolean[this.corrections.size()];
    22                 Arrays.fill(apply, true);
    23                 applyColumn = getColumnCount() - 1;
    24         }
    25 
    26         abstract public int getColumnCount();
    27 
    28         abstract protected boolean isBoldCell(int row, int column);
    29         abstract public String getCorrectionColumnName(int colIndex);
    30         abstract public Object getCorrectionValueAt(int rowIndex, int colIndex);
    31 
    32         public List<C> getCorrections() {
    33                 return corrections;
    34         }
    35        
    36         public int getApplyColumn() {
    37                 return applyColumn;
    38         }
    39        
    40     public boolean getApply(int i) {
    41         return apply[i];
     18    public CorrectionTableModel(List<C> corrections) {
     19        super();
     20        this.corrections = corrections;
     21        apply = new boolean[this.corrections.size()];
     22        Arrays.fill(apply, true);
     23        applyColumn = getColumnCount() - 1;
    4224    }
    4325
    44         public int getRowCount() {
    45         return corrections.size();
     26    abstract public int getColumnCount();
     27
     28    abstract protected boolean isBoldCell(int row, int column);
     29    abstract public String getCorrectionColumnName(int colIndex);
     30    abstract public Object getCorrectionValueAt(int rowIndex, int colIndex);
     31
     32    public List<C> getCorrections() {
     33        return corrections;
    4634    }
    4735
    48         @Override
    49     public Class<?> getColumnClass(int columnIndex) {
    50         if (columnIndex == applyColumn)
    51                 return Boolean.class;
    52         return String.class;
     36    public int getApplyColumn() {
     37        return applyColumn;
    5338    }
    5439
    55         @Override
    56         public String getColumnName(int columnIndex) {
    57         if (columnIndex == applyColumn)
    58                 return tr("Apply?");
    59                
    60                 return getCorrectionColumnName(columnIndex);
    61         }
    62 
    63         @Override
    64     public boolean isCellEditable(int rowIndex, int columnIndex) {
    65         return columnIndex == applyColumn;
     40    public boolean getApply(int i) {
     41        return apply[i];
    6642    }
    6743
    68         @Override
     44    public int getRowCount() {
     45        return corrections.size();
     46    }
     47
     48    @Override
     49    public Class<?> getColumnClass(int columnIndex) {
     50        if (columnIndex == applyColumn)
     51            return Boolean.class;
     52        return String.class;
     53    }
     54
     55    @Override
     56    public String getColumnName(int columnIndex) {
     57        if (columnIndex == applyColumn)
     58            return tr("Apply?");
     59
     60        return getCorrectionColumnName(columnIndex);
     61    }
     62
     63    @Override
     64    public boolean isCellEditable(int rowIndex, int columnIndex) {
     65        return columnIndex == applyColumn;
     66    }
     67
     68    @Override
    6969    public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
    70         if (columnIndex == applyColumn && aValue instanceof Boolean)
    71                 apply[rowIndex] = (Boolean)aValue;
     70        if (columnIndex == applyColumn && aValue instanceof Boolean)
     71            apply[rowIndex] = (Boolean)aValue;
    7272    }
    7373
    7474    public Object getValueAt(int rowIndex, int colIndex) {
    75         if (colIndex == applyColumn)
    76                 return apply[rowIndex];
    77        
    78         return getCorrectionValueAt(rowIndex, colIndex);
    79         }
     75        if (colIndex == applyColumn)
     76            return apply[rowIndex];
     77
     78        return getCorrectionValueAt(rowIndex, colIndex);
     79    }
    8080}
  • trunk/src/org/openstreetmap/josm/corrector/ReverseWayTagCorrector.java

    r1002 r1169  
    2222public class ReverseWayTagCorrector extends TagCorrector<Way> {
    2323
    24         private static class PrefixSuffixSwitcher {
     24    private static class PrefixSuffixSwitcher {
    2525
    26                 private final String a;
    27                 private final String b;
    28                 private final Pattern startPattern;
    29                 private final Pattern endPattern;
     26        private final String a;
     27        private final String b;
     28        private final Pattern startPattern;
     29        private final Pattern endPattern;
    3030
    31                 private final String SEPARATOR = "[:_]?";
    32                
    33                 public PrefixSuffixSwitcher(String a, String b) {
     31        private final String SEPARATOR = "[:_]?";
     32
     33        public PrefixSuffixSwitcher(String a, String b) {
    3434            this.a = a;
    3535            this.b = b;
     
    4040                    SEPARATOR + "(" + a + "|" + b + ")$",
    4141                    Pattern.CASE_INSENSITIVE);
    42                 }
     42        }
    4343
    44                 public String apply(String text) {
    45                         Matcher m = startPattern.matcher(text);
    46                         if (!m.lookingAt())
    47                                 m = endPattern.matcher(text);
     44        public String apply(String text) {
     45            Matcher m = startPattern.matcher(text);
     46            if (!m.lookingAt())
     47                m = endPattern.matcher(text);
    4848
    49                         if (m.lookingAt()) {
    50                                 String leftRight = m.group(1).toLowerCase();
     49            if (m.lookingAt()) {
     50                String leftRight = m.group(1).toLowerCase();
    5151
    52                                 StringBuilder result = new StringBuilder();
    53                                 result.append(text.substring(0, m.start(1)));
    54                                 result.append(leftRight.equals(a) ? b : a);
    55                                 result.append(text.substring(m.end(1)));
    56                                
    57                                 return result.toString();
    58                         }
    59                         return text;
    60                 }
    61         }
     52                StringBuilder result = new StringBuilder();
     53                result.append(text.substring(0, m.start(1)));
     54                result.append(leftRight.equals(a) ? b : a);
     55                result.append(text.substring(m.end(1)));
    6256
    63         private static PrefixSuffixSwitcher[] prefixSuffixSwitchers =
    64                 new PrefixSuffixSwitcher[] {
    65                     new PrefixSuffixSwitcher("left", "right"),
    66                     new PrefixSuffixSwitcher("forward", "backward")
    67                 };
     57                return result.toString();
     58            }
     59            return text;
     60        }
     61    }
    6862
    69         @Override
    70         public Collection<Command> execute(Way way) throws UserCancelException {
    71                 Map<OsmPrimitive, List<TagCorrection>> tagCorrectionsMap =
    72                         new HashMap<OsmPrimitive, List<TagCorrection>>();
     63    private static PrefixSuffixSwitcher[] prefixSuffixSwitchers =
     64            new PrefixSuffixSwitcher[] {
     65                new PrefixSuffixSwitcher("left", "right"),
     66                new PrefixSuffixSwitcher("forward", "backward")
     67            };
    7368
    74                 ArrayList<OsmPrimitive> primitives = new ArrayList<OsmPrimitive>();
    75                 primitives.add(way);
    76                 primitives.addAll(way.nodes);
     69    @Override
     70    public Collection<Command> execute(Way way) throws UserCancelException {
     71        Map<OsmPrimitive, List<TagCorrection>> tagCorrectionsMap =
     72                new HashMap<OsmPrimitive, List<TagCorrection>>();
    7773
    78                 for (OsmPrimitive primitive : primitives) {
    79                         tagCorrectionsMap.put(primitive, new ArrayList<TagCorrection>());
     74        ArrayList<OsmPrimitive> primitives = new ArrayList<OsmPrimitive>();
     75        primitives.add(way);
     76        primitives.addAll(way.nodes);
    8077
    81                         for (String key : primitive.keySet()) {
    82                                 String newKey = key;
    83                                 String value = primitive.get(key);
    84                                 String newValue = value;
     78        for (OsmPrimitive primitive : primitives) {
     79            tagCorrectionsMap.put(primitive, new ArrayList<TagCorrection>());
    8580
    86                                 if (key.equals("oneway")) {
    87                                         if (value.equals("-1"))
    88                                                 newValue = OsmUtils.trueval;
    89                                         else {
    90                                                 Boolean boolValue = OsmUtils.getOsmBoolean(value);
    91                                                 if (boolValue != null && boolValue.booleanValue()) {
    92                                                         newValue = "-1";
    93                                                 }
    94                                         }
    95                                 } else {
    96                                         for (PrefixSuffixSwitcher prefixSuffixSwitcher : prefixSuffixSwitchers) {
    97                                                 newKey = prefixSuffixSwitcher.apply(key);
    98                                                 if (!key.equals(newKey))
    99                                                         break;
    100                                         }
    101                                 }
     81            for (String key : primitive.keySet()) {
     82                String newKey = key;
     83                String value = primitive.get(key);
     84                String newValue = value;
    10285
    103                                 if (!key.equals(newKey) || !value.equals(newValue))
    104                                         tagCorrectionsMap.get(primitive).add(
    105                                                 new TagCorrection(key, value, newKey, newValue));
    106                         }
    107                 }
     86                if (key.equals("oneway")) {
     87                    if (value.equals("-1"))
     88                        newValue = OsmUtils.trueval;
     89                    else {
     90                        Boolean boolValue = OsmUtils.getOsmBoolean(value);
     91                        if (boolValue != null && boolValue.booleanValue()) {
     92                            newValue = "-1";
     93                        }
     94                    }
     95                } else {
     96                    for (PrefixSuffixSwitcher prefixSuffixSwitcher : prefixSuffixSwitchers) {
     97                        newKey = prefixSuffixSwitcher.apply(key);
     98                        if (!key.equals(newKey))
     99                            break;
     100                    }
     101                }
    108102
    109                 Map<OsmPrimitive, List<RoleCorrection>> roleCorrectionMap =
    110                         new HashMap<OsmPrimitive, List<RoleCorrection>>();
    111                 roleCorrectionMap.put(way, new ArrayList<RoleCorrection>());
     103                if (!key.equals(newKey) || !value.equals(newValue))
     104                    tagCorrectionsMap.get(primitive).add(
     105                            new TagCorrection(key, value, newKey, newValue));
     106            }
     107        }
    112108
    113                 for (Relation relation : Main.ds.relations) {
    114                         for (RelationMember member : relation.members) {
    115                                 if (!member.member.realEqual(way, true)
    116                                         || member.role.length() == 0)
    117                                         continue;
     109        Map<OsmPrimitive, List<RoleCorrection>> roleCorrectionMap =
     110                new HashMap<OsmPrimitive, List<RoleCorrection>>();
     111        roleCorrectionMap.put(way, new ArrayList<RoleCorrection>());
    118112
    119                                 boolean found = false;
    120                                 String newRole = null;
    121                                 for (PrefixSuffixSwitcher prefixSuffixSwitcher : prefixSuffixSwitchers) {
    122                                         newRole = prefixSuffixSwitcher.apply(member.role);
    123                                         if (!newRole.equals(member.role)) {
    124                                                 found = true;
    125                                                 break;
    126                                         }
    127                                 }
     113        for (Relation relation : Main.ds.relations) {
     114            for (RelationMember member : relation.members) {
     115                if (!member.member.realEqual(way, true)
     116                        || member.role.length() == 0)
     117                    continue;
    128118
    129                                 if (found)
    130                                         roleCorrectionMap.get(way).add(
    131                                                 new RoleCorrection(relation, member, newRole));
    132                         }
    133                 }
     119                boolean found = false;
     120                String newRole = null;
     121                for (PrefixSuffixSwitcher prefixSuffixSwitcher : prefixSuffixSwitchers) {
     122                    newRole = prefixSuffixSwitcher.apply(member.role);
     123                    if (!newRole.equals(member.role)) {
     124                        found = true;
     125                        break;
     126                    }
     127                }
    134128
    135                 return applyCorrections(tagCorrectionsMap, roleCorrectionMap,
    136                         tr("When reverting this way, following changes to properties "
    137                                 + "of the way and its nodes are suggested in order "
    138                                 + "to maintain data consistency."));
    139         }
     129                if (found)
     130                    roleCorrectionMap.get(way).add(
     131                            new RoleCorrection(relation, member, newRole));
     132            }
     133        }
     134
     135        return applyCorrections(tagCorrectionsMap, roleCorrectionMap,
     136                tr("When reverting this way, following changes to properties "
     137                        + "of the way and its nodes are suggested in order "
     138                        + "to maintain data consistency."));
     139    }
    140140}
  • trunk/src/org/openstreetmap/josm/corrector/TagCorrection.java

    r1000 r1169  
    44public class TagCorrection implements Correction {
    55
    6         public final String oldKey;
    7         public final String newKey;
    8         public final String oldValue;
    9         public final String newValue;
     6    public final String oldKey;
     7    public final String newKey;
     8    public final String oldValue;
     9    public final String newValue;
    1010
    11         public TagCorrection(String oldKey, String oldValue, String newKey,
     11    public TagCorrection(String oldKey, String oldValue, String newKey,
    1212            String newValue) {
    13                 this.oldKey = oldKey;
    14                 this.oldValue = oldValue;
    15                 this.newKey = newKey;
    16                 this.newValue = newValue;
    17         }
     13        this.oldKey = oldKey;
     14        this.oldValue = oldValue;
     15        this.newKey = newKey;
     16        this.newValue = newValue;
     17    }
    1818
    19         public boolean isKeyChanged() {
    20                 return !newKey.equals(oldKey);
    21         }
     19    public boolean isKeyChanged() {
     20        return !newKey.equals(oldKey);
     21    }
    2222
    23         public boolean isValueChanged() {
    24                 return !newValue.equals(oldValue);
    25         }
     23    public boolean isValueChanged() {
     24        return !newValue.equals(oldValue);
     25    }
    2626}
  • trunk/src/org/openstreetmap/josm/corrector/TagCorrectionTable.java

    r1000 r1169  
    77        CorrectionTable<TagCorrectionTableModel> {
    88
    9         public TagCorrectionTable(List<TagCorrection> tagCorrections) {
    10                 super(new TagCorrectionTableModel(tagCorrections));
    11         }
     9    public TagCorrectionTable(List<TagCorrection> tagCorrections) {
     10        super(new TagCorrectionTableModel(tagCorrections));
     11    }
    1212
    1313}
  • trunk/src/org/openstreetmap/josm/corrector/TagCorrectionTableModel.java

    r1000 r1169  
    88public class TagCorrectionTableModel extends CorrectionTableModel<TagCorrection> {
    99
    10         public TagCorrectionTableModel(List<TagCorrection> tagCorrections) {
    11                 super(tagCorrections);
    12         }
     10    public TagCorrectionTableModel(List<TagCorrection> tagCorrections) {
     11        super(tagCorrections);
     12    }
    1313
    14         @Override
    15         public int getColumnCount() {
    16                 return 5;
    17         }
     14    @Override
     15    public int getColumnCount() {
     16        return 5;
     17    }
    1818
    19         @Override
    20         public String getCorrectionColumnName(int colIndex) {
    21                 switch (colIndex) {
    22                 case 0:
    23                         return tr("Old key");
    24                 case 1:
    25                         return tr("Old value");
    26                 case 2:
    27                         return tr("New key");
    28                 case 3:
    29                         return tr("New value");
    30                 }
    31                 return null;
    32         }
     19    @Override
     20    public String getCorrectionColumnName(int colIndex) {
     21        switch (colIndex) {
     22        case 0:
     23            return tr("Old key");
     24        case 1:
     25            return tr("Old value");
     26        case 2:
     27            return tr("New key");
     28        case 3:
     29            return tr("New value");
     30        }
     31        return null;
     32    }
    3333
    3434    public Object getCorrectionValueAt(int rowIndex, int colIndex) {
    35                 TagCorrection tagCorrection = getCorrections().get(rowIndex);
     35        TagCorrection tagCorrection = getCorrections().get(rowIndex);
    3636
    37                 switch (colIndex) {
    38                 case 0:
    39                         return tagCorrection.oldKey;
    40                 case 1:
    41                         return tagCorrection.oldValue;
    42                 case 2:
    43                         return tagCorrection.newKey;
    44                 case 3:
    45                         return tagCorrection.newValue;
    46                 }
    47                 return null;
    48         }
     37        switch (colIndex) {
     38        case 0:
     39            return tagCorrection.oldKey;
     40        case 1:
     41            return tagCorrection.oldValue;
     42        case 2:
     43            return tagCorrection.newKey;
     44        case 3:
     45            return tagCorrection.newValue;
     46        }
     47        return null;
     48    }
    4949
    50         protected boolean isBoldCell(int row, int column) {
    51                 TagCorrection tagCorrection = getCorrections().get(row);
    52                 return (column == 2 && tagCorrection.isKeyChanged())
    53                         || (column == 3 && tagCorrection.isValueChanged());
    54         }
     50    protected boolean isBoldCell(int row, int column) {
     51        TagCorrection tagCorrection = getCorrections().get(row);
     52        return (column == 2 && tagCorrection.isKeyChanged())
     53                || (column == 3 && tagCorrection.isValueChanged());
     54    }
    5555
    5656}
  • trunk/src/org/openstreetmap/josm/corrector/TagCorrector.java

    r1107 r1169  
    3333public abstract class TagCorrector<P extends OsmPrimitive> {
    3434
    35         public abstract Collection<Command> execute(P primitive)
    36             throws UserCancelException;
     35    public abstract Collection<Command> execute(P primitive)
     36        throws UserCancelException;
    3737
    38     private String[] applicationOptions = new String[] { 
    39         tr("Apply selected changes"), 
    40         tr("Don't apply changes"), 
    41         tr("Cancel") 
     38    private String[] applicationOptions = new String[] {
     39        tr("Apply selected changes"),
     40        tr("Don't apply changes"),
     41        tr("Cancel")
    4242    };
    43    
    44         protected Collection<Command> applyCorrections(
    45                 Map<OsmPrimitive, List<TagCorrection>> tagCorrectionsMap,
    46                 Map<OsmPrimitive, List<RoleCorrection>> roleCorrectionMap,
    47                 String description) throws UserCancelException {
    4843
    49                 boolean hasCorrections = false;
    50                 for (List<TagCorrection> tagCorrectionList : tagCorrectionsMap.values()) {
    51                         if (!tagCorrectionList.isEmpty()) {
    52                                 hasCorrections = true;
    53                                 break;
    54                         }
    55                 }
     44    protected Collection<Command> applyCorrections(
     45            Map<OsmPrimitive, List<TagCorrection>> tagCorrectionsMap,
     46            Map<OsmPrimitive, List<RoleCorrection>> roleCorrectionMap,
     47            String description) throws UserCancelException {
    5648
    57                 if (!hasCorrections)
    58                         for (List<RoleCorrection> roleCorrectionList : roleCorrectionMap
    59                                 .values()) {
    60                                 if (!roleCorrectionList.isEmpty()) {
    61                                         hasCorrections = true;
    62                                         break;
    63                                 }
    64                         }
     49        boolean hasCorrections = false;
     50        for (List<TagCorrection> tagCorrectionList : tagCorrectionsMap.values()) {
     51            if (!tagCorrectionList.isEmpty()) {
     52                hasCorrections = true;
     53                break;
     54            }
     55        }
    6556
    66                 if (hasCorrections) {
    67                         Collection<Command> commands = new ArrayList<Command>();
    68                         Map<OsmPrimitive, TagCorrectionTable> tagTableMap =
    69                             new HashMap<OsmPrimitive, TagCorrectionTable>();
    70                         Map<OsmPrimitive, RoleCorrectionTable> roleTableMap =
    71                             new HashMap<OsmPrimitive, RoleCorrectionTable>();
     57        if (!hasCorrections)
     58            for (List<RoleCorrection> roleCorrectionList : roleCorrectionMap
     59                    .values()) {
     60                if (!roleCorrectionList.isEmpty()) {
     61                    hasCorrections = true;
     62                    break;
     63                }
     64            }
    7265
    73                         NameVisitor nameVisitor = new NameVisitor();
     66        if (hasCorrections) {
     67            Collection<Command> commands = new ArrayList<Command>();
     68            Map<OsmPrimitive, TagCorrectionTable> tagTableMap =
     69                new HashMap<OsmPrimitive, TagCorrectionTable>();
     70            Map<OsmPrimitive, RoleCorrectionTable> roleTableMap =
     71                new HashMap<OsmPrimitive, RoleCorrectionTable>();
    7472
    75                         final JPanel p = new JPanel(new GridBagLayout());
     73            NameVisitor nameVisitor = new NameVisitor();
    7674
    77                         final JMultilineLabel label1 = new JMultilineLabel(description);
    78                         label1.setMaxWidth(400);
    79                         p.add(label1, GBC.eop());
     75            final JPanel p = new JPanel(new GridBagLayout());
    8076
    81                         final JMultilineLabel label2 = new JMultilineLabel(
    82                                 tr("Please select which property changes you want to apply."));
    83                         label2.setMaxWidth(400);
    84                         p.add(label2, GBC.eop());
     77            final JMultilineLabel label1 = new JMultilineLabel(description);
     78            label1.setMaxWidth(400);
     79            p.add(label1, GBC.eop());
    8580
    86                         for (OsmPrimitive primitive : tagCorrectionsMap.keySet()) {
    87                                 final List<TagCorrection> tagCorrections = tagCorrectionsMap
    88                                         .get(primitive);
     81            final JMultilineLabel label2 = new JMultilineLabel(
     82                    tr("Please select which property changes you want to apply."));
     83            label2.setMaxWidth(400);
     84            p.add(label2, GBC.eop());
    8985
    90                                 if (tagCorrections.isEmpty())
    91                                         continue;
     86            for (OsmPrimitive primitive : tagCorrectionsMap.keySet()) {
     87                final List<TagCorrection> tagCorrections = tagCorrectionsMap
     88                        .get(primitive);
    9289
    93                                 primitive.visit(nameVisitor);
     90                if (tagCorrections.isEmpty())
     91                    continue;
    9492
    95                                 final JLabel propertiesLabel = new JLabel(tr("Properties of "));
    96                                 p.add(propertiesLabel, GBC.std());
     93                primitive.visit(nameVisitor);
    9794
    98                                 final JLabel primitiveLabel = new JLabel(
    99                                         nameVisitor.name + ":", nameVisitor.icon, JLabel.LEFT);
    100                                 p.add(primitiveLabel, GBC.eol());
     95                final JLabel propertiesLabel = new JLabel(tr("Properties of "));
     96                p.add(propertiesLabel, GBC.std());
    10197
    102                                 final TagCorrectionTable table = new TagCorrectionTable(
    103                                         tagCorrections);
    104                                 final JScrollPane scrollPane = new JScrollPane(table);
    105                                 p.add(scrollPane, GBC.eop());
     98                final JLabel primitiveLabel = new JLabel(
     99                        nameVisitor.name + ":", nameVisitor.icon, JLabel.LEFT);
     100                p.add(primitiveLabel, GBC.eol());
    106101
    107                                 tagTableMap.put(primitive, table);
    108                         }
     102                final TagCorrectionTable table = new TagCorrectionTable(
     103                        tagCorrections);
     104                final JScrollPane scrollPane = new JScrollPane(table);
     105                p.add(scrollPane, GBC.eop());
    109106
    110                         for (OsmPrimitive primitive : roleCorrectionMap.keySet()) {
    111                                 final List<RoleCorrection> roleCorrections = roleCorrectionMap
    112                                         .get(primitive);
    113                                 if (roleCorrections.isEmpty())
    114                                         continue;
     107                tagTableMap.put(primitive, table);
     108            }
    115109
    116                                 primitive.visit(nameVisitor);
     110            for (OsmPrimitive primitive : roleCorrectionMap.keySet()) {
     111                final List<RoleCorrection> roleCorrections = roleCorrectionMap
     112                        .get(primitive);
     113                if (roleCorrections.isEmpty())
     114                    continue;
    117115
    118                                 final JLabel rolesLabel = new JLabel(
    119                                         tr("Roles in relations referring to"));
    120                                 p.add(rolesLabel, GBC.std());
     116                primitive.visit(nameVisitor);
    121117
    122                                 final JLabel primitiveLabel = new JLabel(
    123                                         nameVisitor.name + ":", nameVisitor.icon, JLabel.LEFT);
    124                                 p.add(primitiveLabel, GBC.eol());
     118                final JLabel rolesLabel = new JLabel(
     119                        tr("Roles in relations referring to"));
     120                p.add(rolesLabel, GBC.std());
    125121
    126                                 final RoleCorrectionTable table = new RoleCorrectionTable(
    127                                         roleCorrections);
    128                                 final JScrollPane scrollPane = new JScrollPane(table);
    129                                 p.add(scrollPane, GBC.eop());
     122                final JLabel primitiveLabel = new JLabel(
     123                        nameVisitor.name + ":", nameVisitor.icon, JLabel.LEFT);
     124                p.add(primitiveLabel, GBC.eol());
    130125
    131                                 roleTableMap.put(primitive, table);
    132                         }
     126                final RoleCorrectionTable table = new RoleCorrectionTable(
     127                        roleCorrections);
     128                final JScrollPane scrollPane = new JScrollPane(table);
     129                p.add(scrollPane, GBC.eop());
    133130
    134                         int answer = JOptionPane.showOptionDialog(Main.parent, p,
     131                roleTableMap.put(primitive, table);
     132            }
     133
     134            int answer = JOptionPane.showOptionDialog(Main.parent, p,
    135135                    tr("Automatic tag correction"), JOptionPane.YES_NO_CANCEL_OPTION,
    136                     JOptionPane.PLAIN_MESSAGE, null, 
     136                    JOptionPane.PLAIN_MESSAGE, null,
    137137                    applicationOptions, applicationOptions[0]);
    138138
    139                         if (answer == JOptionPane.YES_OPTION) {
    140                                 for (OsmPrimitive primitive : tagCorrectionsMap.keySet()) {
    141                                         List<TagCorrection> tagCorrections =
     139            if (answer == JOptionPane.YES_OPTION) {
     140                for (OsmPrimitive primitive : tagCorrectionsMap.keySet()) {
     141                    List<TagCorrection> tagCorrections =
    142142                        tagCorrectionsMap.get(primitive);
    143                    
     143
    144144                    // create the clone
    145145                    OsmPrimitive clone = null;
     
    147147                    else if (primitive instanceof Node) clone = new Node((Node)primitive);
    148148                    else if (primitive instanceof Relation) clone = new Relation((Relation)primitive);
    149                    
     149
    150150                    // use this structure to remember keys that have been set already so that
    151151                    // they're not dropped by a later step
    152152                    Set<String> keysChanged = new HashSet<String>();
    153                    
     153
    154154                    // apply all changes to this clone
    155                                         for (int i = 0; i < tagCorrections.size(); i++) {
    156                                                 if (tagTableMap.get(primitive).getCorrectionTableModel().getApply(i)) {
    157                                                         TagCorrection tagCorrection = tagCorrections.get(i);
    158                                                         if (tagCorrection.isKeyChanged() && !keysChanged.contains(tagCorrection.oldKey)) clone.remove(tagCorrection.oldKey);
    159                                                         clone.put(tagCorrection.newKey, tagCorrection.newValue);
    160                                                         keysChanged.add(tagCorrection.newKey);
    161                                                 }
    162                                         }
    163                    
     155                    for (int i = 0; i < tagCorrections.size(); i++) {
     156                        if (tagTableMap.get(primitive).getCorrectionTableModel().getApply(i)) {
     157                            TagCorrection tagCorrection = tagCorrections.get(i);
     158                            if (tagCorrection.isKeyChanged() && !keysChanged.contains(tagCorrection.oldKey)) clone.remove(tagCorrection.oldKey);
     159                            clone.put(tagCorrection.newKey, tagCorrection.newValue);
     160                            keysChanged.add(tagCorrection.newKey);
     161                        }
     162                    }
     163
    164164                    // save the clone
    165165                    if (!keysChanged.isEmpty()) commands.add(new ChangeCommand(primitive, clone));
    166                                 }
    167                                 for (OsmPrimitive primitive : roleCorrectionMap.keySet()) {
    168                                         List<RoleCorrection> roleCorrections = roleCorrectionMap
    169                                                 .get(primitive);
    170                                         for (int i = 0; i < roleCorrections.size(); i++) {
    171                                                 if (roleTableMap.get(primitive)
    172                                                         .getCorrectionTableModel().getApply(i)) {
    173                                                         RoleCorrection roleCorrection = roleCorrections
    174                                                                 .get(i);
    175                                                         Relation newRelation = new Relation(
    176                                                                 roleCorrection.relation);
    177                                                         for (RelationMember member : newRelation.members)
    178                                                                 if (member.equals(roleCorrection.member))
    179                                                                         member.role = roleCorrection.newRole;
    180                                                         commands.add(new ChangeCommand(
    181                                                                 roleCorrection.relation, newRelation));
    182                                                 }
    183                                         }
    184                                 }
    185                         } else if (answer != JOptionPane.NO_OPTION) {
    186                             throw new UserCancelException();
    187                         }
    188                         return commands;
    189                 }
     166                }
     167                for (OsmPrimitive primitive : roleCorrectionMap.keySet()) {
     168                    List<RoleCorrection> roleCorrections = roleCorrectionMap
     169                            .get(primitive);
     170                    for (int i = 0; i < roleCorrections.size(); i++) {
     171                        if (roleTableMap.get(primitive)
     172                                .getCorrectionTableModel().getApply(i)) {
     173                            RoleCorrection roleCorrection = roleCorrections
     174                                    .get(i);
     175                            Relation newRelation = new Relation(
     176                                    roleCorrection.relation);
     177                            for (RelationMember member : newRelation.members)
     178                                if (member.equals(roleCorrection.member))
     179                                    member.role = roleCorrection.newRole;
     180                            commands.add(new ChangeCommand(
     181                                    roleCorrection.relation, newRelation));
     182                        }
     183                    }
     184                }
     185            } else if (answer != JOptionPane.NO_OPTION) {
     186                throw new UserCancelException();
     187            }
     188            return commands;
     189        }
    190190
    191                 return Collections.emptyList();
    192         }
     191        return Collections.emptyList();
     192    }
    193193}
  • trunk/src/org/openstreetmap/josm/data/Bounds.java

    r999 r1169  
    88
    99/**
    10  * This is a simple data class for "rectangular" areas of the world, given in 
     10 * This is a simple data class for "rectangular" areas of the world, given in
    1111 * lat/lon min/max values.
    12  * 
     12 *
    1313 * @author imi
    1414 */
    1515public class Bounds {
    16         /**
    17         * The minimum and maximum coordinates.
    18         */
    19         public LatLon min, max;
     16    /**
     17    * The minimum and maximum coordinates.
     18    */
     19    public LatLon min, max;
    2020
    21         /**
    22         * Construct bounds out of two points
    23         */
    24         public Bounds(LatLon min, LatLon max) {
    25                 this.min = min;
    26                 this.max = max;
    27         }
     21    /**
     22    * Construct bounds out of two points
     23    */
     24    public Bounds(LatLon min, LatLon max) {
     25        this.min = min;
     26        this.max = max;
     27    }
    2828
    29         /**
    30          * Construct bounds that span the whole world.
    31          */
    32         public Bounds() {
    33                 min = new LatLon(-Projection.MAX_LAT, -Projection.MAX_LON);
    34                 max = new LatLon(Projection.MAX_LAT, Projection.MAX_LON);
    35         }
    36        
    37         @Override public String toString() {
    38                 return "Bounds["+min.lat()+","+min.lon()+","+max.lat()+","+max.lon()+"]";
    39         }
     29    /**
     30     * Construct bounds that span the whole world.
     31     */
     32    public Bounds() {
     33        min = new LatLon(-Projection.MAX_LAT, -Projection.MAX_LON);
     34        max = new LatLon(Projection.MAX_LAT, Projection.MAX_LON);
     35    }
    4036
    41         /**
    42          * @return Center of the bounding box.
    43          */
    44         public LatLon center() {
    45                 // FIXME: not sure whether this calculation is right; maybe there is some
    46                 // more complex calculation needed to get a center of a spherical
    47                 // dimension?
    48                 return new LatLon((min.lat()+max.lat())/2, (min.lon()+max.lon())/2);
    49         }
    50        
    51         /**
    52          * Extend the bounds if necessary to include the given point.
    53          */
    54         public void extend(LatLon ll) {
    55                 if (ll.lat() < min.lat() || ll.lon() < min.lon())
    56                         min = new LatLon(Math.min(ll.lat(), min.lat()), Math.min(ll.lon(), min.lon()));
    57                 if (ll.lat() > max.lat() || ll.lon() > max.lon())
    58                         max = new LatLon(Math.max(ll.lat(), max.lat()), Math.max(ll.lon(), max.lon()));
    59         }
    60         /**
    61          * Is the given point within this bounds?
    62          */
    63         public boolean contains(LatLon ll) {
    64                 if (ll.lat() < min.lat() || ll.lon() < min.lon())
    65                         return false;
    66                 if (ll.lat() > max.lat() || ll.lon() > max.lon())
    67                         return false;
    68                 return true;
    69         }
    70        
    71         /**
    72          * Converts the lat/lon bounding box to an object of type Rectangle2D.Double
    73          * @return the bounding box to Rectangle2D.Double
    74          */
    75         public Rectangle2D.Double asRect() {
    76                 return new Rectangle2D.Double(min.lon(), min.lat(), max.lon()-min.lon(), max.lat()-min.lat());
    77         }
     37    @Override public String toString() {
     38        return "Bounds["+min.lat()+","+min.lon()+","+max.lat()+","+max.lon()+"]";
     39    }
     40
     41    /**
     42     * @return Center of the bounding box.
     43     */
     44    public LatLon center() {
     45        // FIXME: not sure whether this calculation is right; maybe there is some
     46        // more complex calculation needed to get a center of a spherical
     47        // dimension?
     48        return new LatLon((min.lat()+max.lat())/2, (min.lon()+max.lon())/2);
     49    }
     50
     51    /**
     52     * Extend the bounds if necessary to include the given point.
     53     */
     54    public void extend(LatLon ll) {
     55        if (ll.lat() < min.lat() || ll.lon() < min.lon())
     56            min = new LatLon(Math.min(ll.lat(), min.lat()), Math.min(ll.lon(), min.lon()));
     57        if (ll.lat() > max.lat() || ll.lon() > max.lon())
     58            max = new LatLon(Math.max(ll.lat(), max.lat()), Math.max(ll.lon(), max.lon()));
     59    }
     60    /**
     61     * Is the given point within this bounds?
     62     */
     63    public boolean contains(LatLon ll) {
     64        if (ll.lat() < min.lat() || ll.lon() < min.lon())
     65            return false;
     66        if (ll.lat() > max.lat() || ll.lon() > max.lon())
     67            return false;
     68        return true;
     69    }
     70
     71    /**
     72     * Converts the lat/lon bounding box to an object of type Rectangle2D.Double
     73     * @return the bounding box to Rectangle2D.Double
     74     */
     75    public Rectangle2D.Double asRect() {
     76        return new Rectangle2D.Double(min.lon(), min.lat(), max.lon()-min.lon(), max.lat()-min.lat());
     77    }
    7878
    7979}
  • trunk/src/org/openstreetmap/josm/data/DataSetChecker.java

    r627 r1169  
    1414public class DataSetChecker {
    1515
    16         public static void check() {
    17                 if (Main.map == null)
    18                         return;
     16    public static void check() {
     17        if (Main.map == null)
     18            return;
    1919
    20                 Set<OsmPrimitive> s = new HashSet<OsmPrimitive>();
    21                 for (Layer l : Main.map.mapView.getAllLayers()) {
    22                         if (l instanceof OsmDataLayer) {
    23                                 for (OsmPrimitive osm : ((OsmDataLayer)l).data.allPrimitives()) {
    24                                         if (s.contains(osm)) {
    25                                 JOptionPane.showMessageDialog(Main.parent, "cross references");
    26                                 return;
    27                                         }
    28                                         s.add(osm);
    29                                 }
    30                         }
    31                 }
    32                
    33                 if (Main.map.mapView.getActiveLayer() instanceof OsmDataLayer) {
    34                         OsmDataLayer l = (OsmDataLayer)Main.map.mapView.getActiveLayer();
    35                         if (l.data != Main.ds) {
    36                                 JOptionPane.showMessageDialog(Main.parent, "Main.ds / active layer mismatch");
    37                                 return;
    38                         }
    39                 }
     20        Set<OsmPrimitive> s = new HashSet<OsmPrimitive>();
     21        for (Layer l : Main.map.mapView.getAllLayers()) {
     22            if (l instanceof OsmDataLayer) {
     23                for (OsmPrimitive osm : ((OsmDataLayer)l).data.allPrimitives()) {
     24                    if (s.contains(osm)) {
     25                        JOptionPane.showMessageDialog(Main.parent, "cross references");
     26                        return;
     27                    }
     28                    s.add(osm);
     29                }
     30            }
     31        }
    4032
    41                 JOptionPane.showMessageDialog(Main.parent, "working");
    42         }
     33        if (Main.map.mapView.getActiveLayer() instanceof OsmDataLayer) {
     34            OsmDataLayer l = (OsmDataLayer)Main.map.mapView.getActiveLayer();
     35            if (l.data != Main.ds) {
     36                JOptionPane.showMessageDialog(Main.parent, "Main.ds / active layer mismatch");
     37                return;
     38            }
     39        }
     40
     41        JOptionPane.showMessageDialog(Main.parent, "working");
     42    }
    4343}
  • trunk/src/org/openstreetmap/josm/data/Preferences.java

    r1159 r1169  
    3535 */
    3636public class Preferences {
    37    
    38     /**
    39     * Internal storage for the preferenced directory. 
     37
     38    /**
     39    * Internal storage for the preferenced directory.
    4040    * Do not access this variable directly!
    4141    * @see #getPreferencesDirFile()
    4242    */
    4343    private File preferencesDirFile = null;
    44      
    45         public static interface PreferenceChangedListener {
    46                 void preferenceChanged(String key, String newValue);
    47         }
    48 
    49         /**
    50         * Class holding one bookmarkentry.
    51         * @author imi
    52         */
    53         public static class Bookmark implements Comparable<Bookmark> {
    54                 public String name;
    55                 public double[] latlon = new double[4]; // minlat, minlon, maxlat, maxlon
    56                 @Override public String toString() {
    57                         return name;
    58                 }
    59                 public int compareTo(Bookmark b) {
    60                         return name.toLowerCase().compareTo(b.name.toLowerCase());
    61                 }
    62         }
    63 
    64         public final ArrayList<PreferenceChangedListener> listener = new ArrayList<PreferenceChangedListener>();
    65 
    66         /**
    67         * Map the property name to the property object.
    68         */
    69         protected final SortedMap<String, String> properties = new TreeMap<String, String>();
    70         protected final SortedMap<String, String> defaults = new TreeMap<String, String>();
    71 
    72         /**
    73         * Override some values on read. This is intended to be used for technology previews
    74         * where we want to temporarily modify things without changing the user's preferences
    75         * file.
    76         */
    77         protected static final SortedMap<String, String> override = new TreeMap<String, String>();
    78         static {
    79                 //override.put("osm-server.version", "0.5");
    80                 //override.put("osm-server.additional-versions", "");
    81                 //override.put("osm-server.url", "http://openstreetmap.gryph.de/api");
    82                 //override.put("plugins", null);
    83         }
    84 
    85         /**
    86         * Return the location of the user defined preferences file
    87         */
    88         public String getPreferencesDir() {
    89                 final String path = getPreferencesDirFile().getPath();
    90                 if (path.endsWith(File.separator))
    91                         return path;
    92                 return path + File.separator;
    93         }
     44
     45    public static interface PreferenceChangedListener {
     46        void preferenceChanged(String key, String newValue);
     47    }
     48
     49    /**
     50    * Class holding one bookmarkentry.
     51    * @author imi
     52    */
     53    public static class Bookmark implements Comparable<Bookmark> {
     54        public String name;
     55        public double[] latlon = new double[4]; // minlat, minlon, maxlat, maxlon
     56        @Override public String toString() {
     57            return name;
     58        }
     59        public int compareTo(Bookmark b) {
     60            return name.toLowerCase().compareTo(b.name.toLowerCase());
     61        }
     62    }
     63
     64    public final ArrayList<PreferenceChangedListener> listener = new ArrayList<PreferenceChangedListener>();
     65
     66    /**
     67    * Map the property name to the property object.
     68    */
     69    protected final SortedMap<String, String> properties = new TreeMap<String, String>();
     70    protected final SortedMap<String, String> defaults = new TreeMap<String, String>();
     71
     72    /**
     73    * Override some values on read. This is intended to be used for technology previews
     74    * where we want to temporarily modify things without changing the user's preferences
     75    * file.
     76    */
     77    protected static final SortedMap<String, String> override = new TreeMap<String, String>();
     78    static {
     79        //override.put("osm-server.version", "0.5");
     80        //override.put("osm-server.additional-versions", "");
     81        //override.put("osm-server.url", "http://openstreetmap.gryph.de/api");
     82        //override.put("plugins", null);
     83    }
     84
     85    /**
     86    * Return the location of the user defined preferences file
     87    */
     88    public String getPreferencesDir() {
     89        final String path = getPreferencesDirFile().getPath();
     90        if (path.endsWith(File.separator))
     91            return path;
     92        return path + File.separator;
     93    }
    9494
    9595    public File getPreferencesDirFile() {
     
    110110        return preferencesDirFile;
    111111    }
    112    
    113         public File getPluginsDirFile() {
    114                 return new File(getPreferencesDirFile(), "plugins");
    115         }
    116 
    117         /**
    118         * @return A list of all existing directories where resources could be stored.
    119         */
    120         public Collection<String> getAllPossiblePreferenceDirs() {
    121                 LinkedList<String> locations = new LinkedList<String>();
    122                 locations.add(Main.pref.getPreferencesDir());
    123                 String s;
    124                 if ((s = System.getenv("JOSM_RESOURCES")) != null) {
    125                         if (!s.endsWith(File.separator))
    126                                 s = s + File.separator;
    127                         locations.add(s);
    128                 }
    129                 if ((s = System.getProperty("josm.resources")) != null) {
    130                         if (!s.endsWith(File.separator))
    131                                 s = s + File.separator;
    132                         locations.add(s);
    133                 }
    134                 String appdata = System.getenv("APPDATA");
    135                 if (System.getenv("ALLUSERSPROFILE") != null && appdata != null
    136                         && appdata.lastIndexOf(File.separator) != -1) {
    137                         appdata = appdata.substring(appdata.lastIndexOf(File.separator));
    138                         locations.add(new File(new File(System.getenv("ALLUSERSPROFILE"),
    139                                 appdata), "JOSM").getPath());
    140                 }
    141                 locations.add("/usr/local/share/josm/");
    142                 locations.add("/usr/local/lib/josm/");
    143                 locations.add("/usr/share/josm/");
    144                 locations.add("/usr/lib/josm/");
    145                 return locations;
    146         }
    147 
    148         synchronized public boolean hasKey(final String key) {
    149                 return override.containsKey(key) ? override.get(key) != null : properties.containsKey(key);
    150         }
    151 
    152         synchronized public String get(final String key) {
    153                 putDefault(key, null);
    154                 if (override.containsKey(key))
    155                         return override.get(key);
    156                 if (!properties.containsKey(key))
    157                         return "";
    158                 return properties.get(key);
    159         }
    160 
    161         synchronized public String get(final String key, final String def) {
    162                 putDefault(key, def);
    163                 if (override.containsKey(key))
    164                         return override.get(key);
    165                 final String prop = properties.get(key);
    166                 if (prop == null || prop.equals(""))
    167                         return def;
    168                 return prop;
    169         }
    170 
    171         synchronized public Map<String, String> getAllPrefix(final String prefix) {
    172                 final Map<String,String> all = new TreeMap<String,String>();
    173                 for (final Entry<String,String> e : properties.entrySet())
    174                         if (e.getKey().startsWith(prefix))
    175                                 all.put(e.getKey(), e.getValue());
    176                 for (final Entry<String,String> e : override.entrySet())
    177                         if (e.getKey().startsWith(prefix))
    178                                 if (e.getValue() == null)
    179                                         all.remove(e.getKey());
    180                                 else
    181                                         all.put(e.getKey(), e.getValue());
    182                 return all;
    183         }
    184 
    185         synchronized public Map<String, String> getDefaults() {
    186                 return defaults;
    187         }
    188 
    189         synchronized public void putDefault(final String key, final String def) {
    190                 if(!defaults.containsKey(key) || defaults.get(key) == null)
    191                         defaults.put(key, def);
    192                 else if(def != null && !defaults.get(key).equals(def))
    193                         System.out.println("Defaults for " + key + " differ: " + def + " != " + defaults.get(key));
    194         }
    195 
    196         synchronized public boolean getBoolean(final String key) {
    197                 putDefault(key, null);
    198                 if (override.containsKey(key))
    199                         return override.get(key) == null ? false : Boolean.parseBoolean(override.get(key));
    200                 return properties.containsKey(key) ? Boolean.parseBoolean(properties.get(key)) : false;
    201         }
    202 
    203         synchronized public boolean getBoolean(final String key, final boolean def) {
    204                 putDefault(key, Boolean.toString(def));
    205                 if (override.containsKey(key))
    206                         return override.get(key) == null ? def : Boolean.parseBoolean(override.get(key));
    207                 return properties.containsKey(key) ? Boolean.parseBoolean(properties.get(key)) : def;
    208         }
    209 
    210         synchronized public void put(final String key, String value) {
    211                 String oldvalue = properties.get(key);
    212                 if(value != null && value.length() == 0)
    213                         value = null;
    214                 if(!((oldvalue == null && value == null) || (value != null
    215                 && oldvalue != null && oldvalue.equals(value))))
    216                 {
    217                         if (value == null)
    218                                 properties.remove(key);
    219                         else
    220                                 properties.put(key, value);
    221                         save();
    222                         firePreferenceChanged(key, value);
    223                 }
    224         }
    225 
    226         synchronized public void put(final String key, final boolean value) {
    227                 put(key, Boolean.toString(value));
    228         }
    229 
    230         private final void firePreferenceChanged(final String key, final String value) {
    231                 for (final PreferenceChangedListener l : listener)
    232                         l.preferenceChanged(key, value);
    233         }
    234 
    235         /**
    236         * Called after every put. In case of a problem, do nothing but output the error
    237         * in log.
    238         */
    239         public void save() {
    240                 try {
    241                         setSystemProperties();
    242                         final PrintWriter out = new PrintWriter(new FileWriter(getPreferencesDir() + "preferences"), false);
    243                         for (final Entry<String, String> e : properties.entrySet()) {
    244                                 out.println(e.getKey() + "=" + e.getValue());
    245                         }
    246                         out.close();
    247                 } catch (final IOException e) {
    248                         e.printStackTrace();
    249                         // do not message anything, since this can be called from strange
    250                         // places.
    251                 }
    252         }
    253 
    254         public void load() throws IOException {
    255                 properties.clear();
    256                 final BufferedReader in = new BufferedReader(new FileReader(getPreferencesDir()+"preferences"));
    257                 int lineNumber = 0;
    258                 ArrayList<Integer> errLines = new ArrayList<Integer>();
    259                 for (String line = in.readLine(); line != null; line = in.readLine(), lineNumber++) {
    260                         final int i = line.indexOf('=');
    261                         if (i == -1 || i == 0) {
    262                                 errLines.add(lineNumber);
    263                                 continue;
    264                         }
    265                         properties.put(line.substring(0,i), line.substring(i+1));
    266                 }
    267                 if (!errLines.isEmpty()) {
    268                         throw new IOException("Malformed config file at lines " + errLines);
    269                 }
    270                 setSystemProperties();
    271         }
    272 
    273         public final void resetToDefault() {
    274                 properties.clear();
    275                 properties.put("projection", "org.openstreetmap.josm.data.projection.Epsg4326");
    276                 properties.put("draw.segment.direction", "true");
    277                 properties.put("draw.wireframe", "false");
    278                 properties.put("layerlist.visible", "true");
    279                 properties.put("propertiesdialog.visible", "true");
    280                 properties.put("selectionlist.visible", "true");
    281                 properties.put("commandstack.visible", "true");
    282                 properties.put("osm-server.url", "http://www.openstreetmap.org/api");
    283                 if (System.getProperty("os.name").toUpperCase().indexOf("WINDOWS") == -1) {
    284                         properties.put("laf", "javax.swing.plaf.metal.MetalLookAndFeel");
    285                 } else {
    286                         properties.put("laf", "com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
    287                 }
    288                 save();
    289         }
    290 
    291         public Collection<Bookmark> loadBookmarks() throws IOException {
    292                 File bookmarkFile = new File(getPreferencesDir()+"bookmarks");
    293                 if (!bookmarkFile.exists())
    294                         bookmarkFile.createNewFile();
    295                 BufferedReader in = new BufferedReader(new FileReader(bookmarkFile));
    296 
    297                 LinkedList<Bookmark> bookmarks = new LinkedList<Bookmark>();
    298                 // use pattern matches to scan text, as text may contain a "," itself
    299                 for (String line = in.readLine(); line != null; line = in.readLine()) {
    300                         Matcher m = Pattern.compile("^(.+),(-?\\d+.\\d+),(-?\\d+.\\d+),(-?\\d+.\\d+),(-?\\d+.\\d+)$").matcher(line);
    301                         if(m.matches())
    302                         {
    303                                 Bookmark b = new Bookmark();
    304                                 b.name = m.group(1);
    305                                 for (int i = 0; i < b.latlon.length; ++i)
    306                                         b.latlon[i] = Double.parseDouble(m.group(i+2));
    307                                 bookmarks.add(b);
    308                         }
    309                 }
    310                 in.close();
    311                 Collections.sort(bookmarks);
    312                 return bookmarks;
    313         }
    314 
    315         public void saveBookmarks(Collection<Bookmark> bookmarks) throws IOException {
    316                 File bookmarkFile = new File(Main.pref.getPreferencesDir()+"bookmarks");
    317                 if (!bookmarkFile.exists())
    318                         bookmarkFile.createNewFile();
    319                 PrintWriter out = new PrintWriter(new FileWriter(bookmarkFile));
    320                 for (Bookmark b : bookmarks) {
    321                         out.print(b.name+",");
    322                         for (int i = 0; i < b.latlon.length; ++i)
    323                                 out.print(b.latlon[i]+(i<b.latlon.length-1?",":""));
    324                         out.println();
    325                 }
    326                 out.close();
    327         }
    328 
    329         /**
    330         * Convenience method for accessing colour preferences.
    331         *
    332         * @param colName name of the colour
    333         * @param def default value
    334         * @return a Color object for the configured colour, or the default value if none configured.
    335         */
    336         synchronized public Color getColor(String colName, Color def) {
    337                 String colStr = get("color."+colName);
    338                 if (colStr.equals("")) {
    339                         put("color."+colName, ColorHelper.color2html(def));
    340                         return def;
    341                 }
    342                 return ColorHelper.html2color(colStr);
    343         }
    344 
    345         // only for compatibility. Don't use any more.
    346         @Deprecated
    347         public static Color getPreferencesColor(String colName, Color def)
    348         {
    349                 return Main.pref.getColor(colName, def);
    350         }
    351 
    352         /**
    353         * Convenience method for accessing colour preferences.
    354         *
    355         * @param colName name of the colour
    356         * @param specName name of the special colour settings
    357         * @param def default value
    358         * @return a Color object for the configured colour, or the default value if none configured.
    359         */
    360         synchronized public Color getColor(String colName, String specName, Color def) {
    361                 String colStr = get("color."+specName);
    362                 if(colStr.equals(""))
    363                 {
    364                         colStr = get("color."+colName);
    365                         if (colStr.equals("")) {
    366                                 put("color."+colName, ColorHelper.color2html(def));
    367                                 return def;
    368                         }
    369                 }
    370                 putDefault("color."+colName, ColorHelper.color2html(def));
    371                 return ColorHelper.html2color(colStr);
    372         }
    373 
    374         synchronized public void putColor(String colName, Color val) {
    375                 put("color."+colName, val != null ? ColorHelper.color2html(val) : null);
    376         }
    377 
    378         synchronized public int getInteger(String key, int def) {
    379                 putDefault(key, Integer.toString(def));
    380                 String v = get(key);
    381                 if(null == v)
    382                         return def;
    383 
    384                 try {
    385                         return Integer.parseInt(v);
    386                 } catch(NumberFormatException e) {
    387                         // fall out
    388                 }
    389                 return def;
    390         }
    391 
    392         synchronized public long getLong(String key, long def) {
    393                 putDefault(key, Long.toString(def));
    394                 String v = get(key);
    395                 if(null == v)
    396                         return def;
    397 
    398                 try {
    399                         return Long.parseLong(v);
    400                 } catch(NumberFormatException e) {
    401                         // fall out
    402                 }
    403                 return def;
    404         }
    405 
    406         synchronized public double getDouble(String key, double def) {
    407                 putDefault(key, Double.toString(def));
    408                 String v = get(key);
    409                 if(null == v)
    410                         return def;
    411 
    412                 try {
    413                         return Double.parseDouble(v);
    414                 } catch(NumberFormatException e) {
    415                         // fall out
    416                 }
    417                 return def;
    418         }
    419 
    420         synchronized public Collection<String> getCollection(String key, Collection<String> def) {
    421                 String s = get(key);
    422                 if(s != null && s.length() != 0)
    423                 {
    424                         /* handle old comma separated stuff - remove in future */
    425                         if(s.indexOf(',') >= 0)
    426                                 return Arrays.asList(s.split(","));
    427                         /* handle space separated stuff - remove in future */
    428                         else if(s.indexOf(' ') >= 0)
    429                                 return Arrays.asList(s.split(","));
    430                         else
    431                                 return Arrays.asList(s.split(";"));
    432                 }
    433                 else if(def != null)
    434                         return def;
    435                 return null;
    436         }
    437         synchronized public void removeFromCollection(String key, String value) {
    438                 ArrayList<String> a = new ArrayList<String>(getCollection(key, null));
    439                 if(a != null)
    440                 {
    441                         a.remove(value);
    442                         putCollection(key, a);
    443                 }
    444         }
    445         synchronized public void putCollection(String key, Collection<String> val) {
    446                 String s = null;
    447                 if(val != null)
    448                 {
    449                         for(String a : val)
    450                         {
    451                                 if(s != null)
    452                                         s += ";" + a;
    453                                 else
    454                                         s = a;
    455                         }
    456                 }
    457 
    458                 put(key, s);
    459         }
    460 
    461         private void setSystemProperties() {
    462                 Properties sysProp = System.getProperties();
    463                 if (getBoolean(ProxyPreferences.PROXY_ENABLE)) {
    464                         sysProp.put("proxySet", "true");
    465                         sysProp.put("http.proxyHost", get(ProxyPreferences.PROXY_HOST));
    466                         sysProp.put("proxyPort", get(ProxyPreferences.PROXY_PORT));
    467                         if (!getBoolean(ProxyPreferences.PROXY_ANONYMOUS)) {
    468                                 sysProp.put("proxyUser", get(ProxyPreferences.PROXY_USER));
    469                                 sysProp.put("proxyPassword", get(ProxyPreferences.PROXY_PASS));
    470                         }
    471                 }/* else {
    472                         sysProp.put("proxySet", "false");
    473                         sysProp.remove("http.proxyHost");
    474                         sysProp.remove("proxyPort");
    475                         sysProp.remove("proxyUser");
    476                         sysProp.remove("proxyPassword");
    477                 }*/
    478                 System.setProperties(sysProp);
    479         }
     112
     113    public File getPluginsDirFile() {
     114        return new File(getPreferencesDirFile(), "plugins");
     115    }
     116
     117    /**
     118    * @return A list of all existing directories where resources could be stored.
     119    */
     120    public Collection<String> getAllPossiblePreferenceDirs() {
     121        LinkedList<String> locations = new LinkedList<String>();
     122        locations.add(Main.pref.getPreferencesDir());
     123        String s;
     124        if ((s = System.getenv("JOSM_RESOURCES")) != null) {
     125            if (!s.endsWith(File.separator))
     126                s = s + File.separator;
     127            locations.add(s);
     128        }
     129        if ((s = System.getProperty("josm.resources")) != null) {
     130            if (!s.endsWith(File.separator))
     131                s = s + File.separator;
     132            locations.add(s);
     133        }
     134        String appdata = System.getenv("APPDATA");
     135        if (System.getenv("ALLUSERSPROFILE") != null && appdata != null
     136                && appdata.lastIndexOf(File.separator) != -1) {
     137            appdata = appdata.substring(appdata.lastIndexOf(File.separator));
     138            locations.add(new File(new File(System.getenv("ALLUSERSPROFILE"),
     139                    appdata), "JOSM").getPath());
     140        }
     141        locations.add("/usr/local/share/josm/");
     142        locations.add("/usr/local/lib/josm/");
     143        locations.add("/usr/share/josm/");
     144        locations.add("/usr/lib/josm/");
     145        return locations;
     146    }
     147
     148    synchronized public boolean hasKey(final String key) {
     149        return override.containsKey(key) ? override.get(key) != null : properties.containsKey(key);
     150    }
     151
     152    synchronized public String get(final String key) {
     153        putDefault(key, null);
     154        if (override.containsKey(key))
     155            return override.get(key);
     156        if (!properties.containsKey(key))
     157            return "";
     158        return properties.get(key);
     159    }
     160
     161    synchronized public String get(final String key, final String def) {
     162        putDefault(key, def);
     163        if (override.containsKey(key))
     164            return override.get(key);
     165        final String prop = properties.get(key);
     166        if (prop == null || prop.equals(""))
     167            return def;
     168        return prop;
     169    }
     170
     171    synchronized public Map<String, String> getAllPrefix(final String prefix) {
     172        final Map<String,String> all = new TreeMap<String,String>();
     173        for (final Entry<String,String> e : properties.entrySet())
     174            if (e.getKey().startsWith(prefix))
     175                all.put(e.getKey(), e.getValue());
     176        for (final Entry<String,String> e : override.entrySet())
     177            if (e.getKey().startsWith(prefix))
     178                if (e.getValue() == null)
     179                    all.remove(e.getKey());
     180                else
     181                    all.put(e.getKey(), e.getValue());
     182        return all;
     183    }
     184
     185    synchronized public Map<String, String> getDefaults() {
     186        return defaults;
     187    }
     188
     189    synchronized public void putDefault(final String key, final String def) {
     190        if(!defaults.containsKey(key) || defaults.get(key) == null)
     191            defaults.put(key, def);
     192        else if(def != null && !defaults.get(key).equals(def))
     193            System.out.println("Defaults for " + key + " differ: " + def + " != " + defaults.get(key));
     194    }
     195
     196    synchronized public boolean getBoolean(final String key) {
     197        putDefault(key, null);
     198        if (override.containsKey(key))
     199            return override.get(key) == null ? false : Boolean.parseBoolean(override.get(key));
     200        return properties.containsKey(key) ? Boolean.parseBoolean(properties.get(key)) : false;
     201    }
     202
     203    synchronized public boolean getBoolean(final String key, final boolean def) {
     204        putDefault(key, Boolean.toString(def));
     205        if (override.containsKey(key))
     206            return override.get(key) == null ? def : Boolean.parseBoolean(override.get(key));
     207        return properties.containsKey(key) ? Boolean.parseBoolean(properties.get(key)) : def;
     208    }
     209
     210    synchronized public void put(final String key, String value) {
     211        String oldvalue = properties.get(key);
     212        if(value != null && value.length() == 0)
     213            value = null;
     214        if(!((oldvalue == null && value == null) || (value != null
     215        && oldvalue != null && oldvalue.equals(value))))
     216        {
     217            if (value == null)
     218                properties.remove(key);
     219            else
     220                properties.put(key, value);
     221            save();
     222            firePreferenceChanged(key, value);
     223        }
     224    }
     225
     226    synchronized public void put(final String key, final boolean value) {
     227        put(key, Boolean.toString(value));
     228    }
     229
     230    private final void firePreferenceChanged(final String key, final String value) {
     231        for (final PreferenceChangedListener l : listener)
     232            l.preferenceChanged(key, value);
     233    }
     234
     235    /**
     236    * Called after every put. In case of a problem, do nothing but output the error
     237    * in log.
     238    */
     239    public void save() {
     240        try {
     241            setSystemProperties();
     242            final PrintWriter out = new PrintWriter(new FileWriter(getPreferencesDir() + "preferences"), false);
     243            for (final Entry<String, String> e : properties.entrySet()) {
     244                out.println(e.getKey() + "=" + e.getValue());
     245            }
     246            out.close();
     247        } catch (final IOException e) {
     248            e.printStackTrace();
     249            // do not message anything, since this can be called from strange
     250            // places.
     251        }
     252    }
     253
     254    public void load() throws IOException {
     255        properties.clear();
     256        final BufferedReader in = new BufferedReader(new FileReader(getPreferencesDir()+"preferences"));
     257        int lineNumber = 0;
     258        ArrayList<Integer> errLines = new ArrayList<Integer>();
     259        for (String line = in.readLine(); line != null; line = in.readLine(), lineNumber++) {
     260            final int i = line.indexOf('=');
     261            if (i == -1 || i == 0) {
     262                errLines.add(lineNumber);
     263                continue;
     264            }
     265            properties.put(line.substring(0,i), line.substring(i+1));
     266        }
     267        if (!errLines.isEmpty()) {
     268            throw new IOException("Malformed config file at lines " + errLines);
     269        }
     270        setSystemProperties();
     271    }
     272
     273    public final void resetToDefault() {
     274        properties.clear();
     275        properties.put("projection", "org.openstreetmap.josm.data.projection.Epsg4326");
     276        properties.put("draw.segment.direction", "true");
     277        properties.put("draw.wireframe", "false");
     278        properties.put("layerlist.visible", "true");
     279        properties.put("propertiesdialog.visible", "true");
     280        properties.put("selectionlist.visible", "true");
     281        properties.put("commandstack.visible", "true");
     282        properties.put("osm-server.url", "http://www.openstreetmap.org/api");
     283        if (System.getProperty("os.name").toUpperCase().indexOf("WINDOWS") == -1) {
     284            properties.put("laf", "javax.swing.plaf.metal.MetalLookAndFeel");
     285        } else {
     286            properties.put("laf", "com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
     287        }
     288        save();
     289    }
     290
     291    public Collection<Bookmark> loadBookmarks() throws IOException {
     292        File bookmarkFile = new File(getPreferencesDir()+"bookmarks");
     293        if (!bookmarkFile.exists())
     294            bookmarkFile.createNewFile();
     295        BufferedReader in = new BufferedReader(new FileReader(bookmarkFile));
     296
     297        LinkedList<Bookmark> bookmarks = new LinkedList<Bookmark>();
     298        // use pattern matches to scan text, as text may contain a "," itself
     299        for (String line = in.readLine(); line != null; line = in.readLine()) {
     300            Matcher m = Pattern.compile("^(.+),(-?\\d+.\\d+),(-?\\d+.\\d+),(-?\\d+.\\d+),(-?\\d+.\\d+)$").matcher(line);
     301            if(m.matches())
     302            {
     303                Bookmark b = new Bookmark();
     304                b.name = m.group(1);
     305                for (int i = 0; i < b.latlon.length; ++i)
     306                    b.latlon[i] = Double.parseDouble(m.group(i+2));
     307                bookmarks.add(b);
     308            }
     309        }
     310        in.close();
     311        Collections.sort(bookmarks);
     312        return bookmarks;
     313    }
     314
     315    public void saveBookmarks(Collection<Bookmark> bookmarks) throws IOException {
     316        File bookmarkFile = new File(Main.pref.getPreferencesDir()+"bookmarks");
     317        if (!bookmarkFile.exists())
     318            bookmarkFile.createNewFile();
     319        PrintWriter out = new PrintWriter(new FileWriter(bookmarkFile));
     320        for (Bookmark b : bookmarks) {
     321            out.print(b.name+",");
     322            for (int i = 0; i < b.latlon.length; ++i)
     323                out.print(b.latlon[i]+(i<b.latlon.length-1?",":""));
     324            out.println();
     325        }
     326        out.close();
     327    }
     328
     329    /**
     330    * Convenience method for accessing colour preferences.
     331    *
     332    * @param colName name of the colour
     333    * @param def default value
     334    * @return a Color object for the configured colour, or the default value if none configured.
     335    */
     336    synchronized public Color getColor(String colName, Color def) {
     337        String colStr = get("color."+colName);
     338        if (colStr.equals("")) {
     339            put("color."+colName, ColorHelper.color2html(def));
     340            return def;
     341        }
     342        return ColorHelper.html2color(colStr);
     343    }
     344
     345    // only for compatibility. Don't use any more.
     346    @Deprecated
     347    public static Color getPreferencesColor(String colName, Color def)
     348    {
     349        return Main.pref.getColor(colName, def);
     350    }
     351
     352    /**
     353    * Convenience method for accessing colour preferences.
     354    *
     355    * @param colName name of the colour
     356    * @param specName name of the special colour settings
     357    * @param def default value
     358    * @return a Color object for the configured colour, or the default value if none configured.
     359    */
     360    synchronized public Color getColor(String colName, String specName, Color def) {
     361        String colStr = get("color."+specName);
     362        if(colStr.equals(""))
     363        {
     364            colStr = get("color."+colName);
     365            if (colStr.equals("")) {
     366                put("color."+colName, ColorHelper.color2html(def));
     367                return def;
     368            }
     369        }
     370        putDefault("color."+colName, ColorHelper.color2html(def));
     371        return ColorHelper.html2color(colStr);
     372    }
     373
     374    synchronized public void putColor(String colName, Color val) {
     375        put("color."+colName, val != null ? ColorHelper.color2html(val) : null);
     376    }
     377
     378    synchronized public int getInteger(String key, int def) {
     379        putDefault(key, Integer.toString(def));
     380        String v = get(key);
     381        if(null == v)
     382            return def;
     383
     384        try {
     385            return Integer.parseInt(v);
     386        } catch(NumberFormatException e) {
     387            // fall out
     388        }
     389        return def;
     390    }
     391
     392    synchronized public long getLong(String key, long def) {
     393        putDefault(key, Long.toString(def));
     394        String v = get(key);
     395        if(null == v)
     396            return def;
     397
     398        try {
     399            return Long.parseLong(v);
     400        } catch(NumberFormatException e) {
     401            // fall out
     402        }
     403        return def;
     404    }
     405
     406    synchronized public double getDouble(String key, double def) {
     407        putDefault(key, Double.toString(def));
     408        String v = get(key);
     409        if(null == v)
     410            return def;
     411
     412        try {
     413            return Double.parseDouble(v);
     414        } catch(NumberFormatException e) {
     415            // fall out
     416        }
     417        return def;
     418    }
     419
     420    synchronized public Collection<String> getCollection(String key, Collection<String> def) {
     421        String s = get(key);
     422        if(s != null && s.length() != 0)
     423        {
     424            /* handle old comma separated stuff - remove in future */
     425            if(s.indexOf(',') >= 0)
     426                return Arrays.asList(s.split(","));
     427            /* handle space separated stuff - remove in future */
     428            else if(s.indexOf(' ') >= 0)
     429                return Arrays.asList(s.split(","));
     430            else
     431                return Arrays.asList(s.split(";"));
     432        }
     433        else if(def != null)
     434            return def;
     435        return null;
     436    }
     437    synchronized public void removeFromCollection(String key, String value) {
     438        ArrayList<String> a = new ArrayList<String>(getCollection(key, null));
     439        if(a != null)
     440        {
     441            a.remove(value);
     442            putCollection(key, a);
     443        }
     444    }
     445    synchronized public void putCollection(String key, Collection<String> val) {
     446        String s = null;
     447        if(val != null)
     448        {
     449            for(String a : val)
     450            {
     451                if(s != null)
     452                    s += ";" + a;
     453                else
     454                    s = a;
     455            }
     456        }
     457
     458        put(key, s);
     459    }
     460
     461    private void setSystemProperties() {
     462        Properties sysProp = System.getProperties();
     463        if (getBoolean(ProxyPreferences.PROXY_ENABLE)) {
     464            sysProp.put("proxySet", "true");
     465            sysProp.put("http.proxyHost", get(ProxyPreferences.PROXY_HOST));
     466            sysProp.put("proxyPort", get(ProxyPreferences.PROXY_PORT));
     467            if (!getBoolean(ProxyPreferences.PROXY_ANONYMOUS)) {
     468                sysProp.put("proxyUser", get(ProxyPreferences.PROXY_USER));
     469                sysProp.put("proxyPassword", get(ProxyPreferences.PROXY_PASS));
     470            }
     471        }/* else {
     472            sysProp.put("proxySet", "false");
     473            sysProp.remove("http.proxyHost");
     474            sysProp.remove("proxyPort");
     475            sysProp.remove("proxyUser");
     476            sysProp.remove("proxyPassword");
     477        }*/
     478        System.setProperties(sysProp);
     479    }
    480480}
  • trunk/src/org/openstreetmap/josm/data/SelectionChangedListener.java

    r655 r1169  
    1010 * a selection of any data member changes, the dataSet gets informed about this
    1111 * and fires a selectionChanged event.
    12  * 
     12 *
    1313 * Note that these events are not fired immediately but are inserted in the
    1414 * Swing event queue and packed together. So only one selection changed event
    1515 * is issued within a one message dispatch routine.
    16  * 
     16 *
    1717 * @author imi
    1818 */
    1919public interface SelectionChangedListener {
    2020
    21         /**
    22         * Informs the listener that the selection in the dataset has changed.
    23         * @param newSelection The new selection.
    24         */
    25         public void selectionChanged(Collection<? extends OsmPrimitive> newSelection);
     21    /**
     22    * Informs the listener that the selection in the dataset has changed.
     23    * @param newSelection The new selection.
     24    */
     25    public void selectionChanged(Collection<? extends OsmPrimitive> newSelection);
    2626}
  • trunk/src/org/openstreetmap/josm/data/ServerSidePreferences.java

    r733 r1169  
    3232 * This class tweak the Preferences class to provide server side preference settings, as example
    3333 * used in the applet version.
    34  * 
     34 *
    3535 * @author Imi
    3636 */
    3737public class ServerSidePreferences extends Preferences {
    3838
    39         private final Connection connection;
     39    private final Connection connection;
    4040
    41         private class Connection extends OsmConnection {
    42                 URL serverUrl;
    43                 public Connection(URL serverUrl) {
    44                         this.serverUrl = serverUrl;
    45                 }
    46                 public String download() {
    47                         try {
    48                                 System.out.println("reading preferences from "+serverUrl);
    49                                 URLConnection con = serverUrl.openConnection();
    50                                 if (con instanceof HttpURLConnection) addAuth((HttpURLConnection) con);
    51                                 con.connect();
    52                                 BufferedReader reader = new BufferedReader(new InputStreamReader(con.getInputStream()));
    53                                 StringBuilder b = new StringBuilder();
    54                                 for (String line = reader.readLine(); line != null; line = reader.readLine()) {
    55                                         b.append(line);
    56                                         b.append("\n");
    57                                 }
    58                                 if (con instanceof HttpURLConnection) ((HttpURLConnection) con).disconnect();
    59                                 return b.toString();
    60                         } catch (IOException e) {
    61                                 e.printStackTrace();
    62                         }
    63                         return null;
    64                 }
    65                 public void upload(String s) {
    66                         try {
    67                                 URL u = new URL(getPreferencesDir());
    68                                 System.out.println("uploading preferences to "+u);
    69                                 HttpURLConnection con = (HttpURLConnection)u.openConnection();
    70                                 con.addRequestProperty("Authorization", "Basic "+Base64.encode(get("osm-server.username")+":"+get("osm-server.password")));
    71                                 con.setRequestMethod("POST");
    72                                 con.setDoOutput(true);
    73                                 con.connect();
    74                                 PrintWriter out = new PrintWriter(new OutputStreamWriter(con.getOutputStream()));
    75                                 out.println(s);
    76                                 out.close();
    77                                 con.getInputStream().close();
    78                                 con.disconnect();
    79                                 JOptionPane.showMessageDialog(Main.parent, tr("Preferences stored on {0}", u.getHost()));
    80                         } catch (Exception e) {
    81                                 e.printStackTrace();
    82                                 JOptionPane.showMessageDialog(Main.parent, tr("Could not upload preferences. Reason: {0}", e.getMessage()));
    83                         }
    84                 }
    85         }
     41    private class Connection extends OsmConnection {
     42        URL serverUrl;
     43        public Connection(URL serverUrl) {
     44            this.serverUrl = serverUrl;
     45        }
     46        public String download() {
     47            try {
     48                System.out.println("reading preferences from "+serverUrl);
     49                URLConnection con = serverUrl.openConnection();
     50                if (con instanceof HttpURLConnection) addAuth((HttpURLConnection) con);
     51                con.connect();
     52                BufferedReader reader = new BufferedReader(new InputStreamReader(con.getInputStream()));
     53                StringBuilder b = new StringBuilder();
     54                for (String line = reader.readLine(); line != null; line = reader.readLine()) {
     55                    b.append(line);
     56                    b.append("\n");
     57                }
     58                if (con instanceof HttpURLConnection) ((HttpURLConnection) con).disconnect();
     59                return b.toString();
     60            } catch (IOException e) {
     61                e.printStackTrace();
     62            }
     63            return null;
     64        }
     65        public void upload(String s) {
     66            try {
     67                URL u = new URL(getPreferencesDir());
     68                System.out.println("uploading preferences to "+u);
     69                HttpURLConnection con = (HttpURLConnection)u.openConnection();
     70                con.addRequestProperty("Authorization", "Basic "+Base64.encode(get("osm-server.username")+":"+get("osm-server.password")));
     71                con.setRequestMethod("POST");
     72                con.setDoOutput(true);
     73                con.connect();
     74                PrintWriter out = new PrintWriter(new OutputStreamWriter(con.getOutputStream()));
     75                out.println(s);
     76                out.close();
     77                con.getInputStream().close();
     78                con.disconnect();
     79                JOptionPane.showMessageDialog(Main.parent, tr("Preferences stored on {0}", u.getHost()));
     80            } catch (Exception e) {
     81                e.printStackTrace();
     82                JOptionPane.showMessageDialog(Main.parent, tr("Could not upload preferences. Reason: {0}", e.getMessage()));
     83            }
     84        }
     85    }
    8686
    87         public ServerSidePreferences(URL serverUrl) {
    88                 Connection connection = null;
    89                 try {
    90                         connection = new Connection(new URL(serverUrl+"user/preferences"));
    91                 } catch (MalformedURLException e) {
    92                         e.printStackTrace();
    93                         JOptionPane.showMessageDialog(Main.parent, tr("Could not load preferences from server."));
    94                 }
    95                 this.connection = connection;
    96         }
     87    public ServerSidePreferences(URL serverUrl) {
     88        Connection connection = null;
     89        try {
     90            connection = new Connection(new URL(serverUrl+"user/preferences"));
     91        } catch (MalformedURLException e) {
     92            e.printStackTrace();
     93            JOptionPane.showMessageDialog(Main.parent, tr("Could not load preferences from server."));
     94        }
     95        this.connection = connection;
     96    }
    9797
    98         @Override public String getPreferencesDir() {
    99                 return connection.serverUrl.toString();
    100         }
     98    @Override public String getPreferencesDir() {
     99        return connection.serverUrl.toString();
     100    }
    101101
    102         /**
    103         * Do nothing on load. Preferences are loaded with download().
    104         */
    105         @Override public void load() {
    106         }
     102    /**
     103    * Do nothing on load. Preferences are loaded with download().
     104    */
     105    @Override public void load() {
     106    }
    107107
    108         /**
    109         * Do nothing on save. Preferences are uploaded using upload().
    110         */
    111         @Override public void save() {
    112         }
     108    /**
     109    * Do nothing on save. Preferences are uploaded using upload().
     110    */
     111    @Override public void save() {
     112    }
    113113
    114         public static class Prop {
    115                 public String key;
    116                 public String value;
    117         }
     114    public static class Prop {
     115        public String key;
     116        public String value;
     117    }
    118118
    119         public void download(String userName, String password) {
    120                 resetToDefault();
    121                 if (!properties.containsKey("osm-server.username") && userName != null)
    122                         properties.put("osm-server.username", userName);
    123                 if (!properties.containsKey("osm-server.password") && password != null)
    124                         properties.put("osm-server.password", password);
    125                 String cont = connection.download();
    126                 if (cont == null) return;
    127                 Reader in = new StringReader(cont);
    128                 try {
    129                         XmlObjectParser.Uniform<Prop> parser = new XmlObjectParser.Uniform<Prop>(in, "tag", Prop.class);
    130                         for (Prop p : parser)
    131                                 properties.put(p.key, p.value);
    132                 } catch (RuntimeException e) {
    133                         e.printStackTrace();
    134                 }
    135         }
     119    public void download(String userName, String password) {
     120        resetToDefault();
     121        if (!properties.containsKey("osm-server.username") && userName != null)
     122            properties.put("osm-server.username", userName);
     123        if (!properties.containsKey("osm-server.password") && password != null)
     124            properties.put("osm-server.password", password);
     125        String cont = connection.download();
     126        if (cont == null) return;
     127        Reader in = new StringReader(cont);
     128        try {
     129            XmlObjectParser.Uniform<Prop> parser = new XmlObjectParser.Uniform<Prop>(in, "tag", Prop.class);
     130            for (Prop p : parser)
     131                properties.put(p.key, p.value);
     132        } catch (RuntimeException e) {
     133            e.printStackTrace();
     134        }
     135    }
    136136
    137         /**
    138         * Use this instead of save() for the ServerSidePreferences, since uploads
    139         * are costly while save is called often.
    140          *
    141         * This is triggered by an explicit menu option.
    142         */
    143         public void upload() {
    144                 StringBuilder b = new StringBuilder("<preferences>\n");
    145                 for (Entry<String, String> p : properties.entrySet()) {
    146                         if (p.getKey().equals("osm-server.password"))
    147                                 continue; // do not upload password. It would get stored in plain!
    148                         b.append("<tag key='");
    149                         b.append(XmlWriter.encode(p.getKey()));
    150                         b.append("' value='");
    151                         b.append(XmlWriter.encode(p.getValue()));
    152                         b.append("' />\n");
    153                 }
    154                 b.append("</preferences>");
    155                 connection.upload(b.toString());
    156         }
     137    /**
     138    * Use this instead of save() for the ServerSidePreferences, since uploads
     139    * are costly while save is called often.
     140     *
     141    * This is triggered by an explicit menu option.
     142    */
     143    public void upload() {
     144        StringBuilder b = new StringBuilder("<preferences>\n");
     145        for (Entry<String, String> p : properties.entrySet()) {
     146            if (p.getKey().equals("osm-server.password"))
     147                continue; // do not upload password. It would get stored in plain!
     148            b.append("<tag key='");
     149            b.append(XmlWriter.encode(p.getKey()));
     150            b.append("' value='");
     151            b.append(XmlWriter.encode(p.getValue()));
     152            b.append("' />\n");
     153        }
     154        b.append("</preferences>");
     155        connection.upload(b.toString());
     156    }
    157157
    158         @Override public Collection<Bookmark> loadBookmarks() {
    159                 try {
    160                         Collection<Bookmark> bookmarks;
    161                         BufferedReader in = new BufferedReader(new InputStreamReader(new URL("http://"+connection.serverUrl.getHost()+"/josm/bookmarks").openStream()));
    162                         bookmarks = new LinkedList<Bookmark>();
    163                         for (String line = in.readLine(); line != null; line = in.readLine()) {
    164                                 StringTokenizer st = new StringTokenizer(line, ",");
    165                                 if (st.countTokens() < 5)
    166                                         continue;
    167                                 Bookmark b = new Bookmark();
    168                                 b.name = st.nextToken();
    169                                 try {
    170                                         for (int i = 0; i < b.latlon.length; ++i)
    171                                                 b.latlon[i] = Double.parseDouble(st.nextToken());
    172                                         bookmarks.add(b);
    173                                 } catch (NumberFormatException x) {
    174                                         // line not parsed
    175                                 }
    176                         }
    177                         in.close();
    178                         return bookmarks;
    179                 } catch (MalformedURLException e) {
    180                         e.printStackTrace();
    181                 } catch (IllegalArgumentException e) {
    182                         e.printStackTrace();
    183                 } catch (IOException e) {
    184                         e.printStackTrace();
    185                 }
    186                 return Collections.emptyList();
    187         }
     158    @Override public Collection<Bookmark> loadBookmarks() {
     159        try {
     160            Collection<Bookmark> bookmarks;
     161            BufferedReader in = new BufferedReader(new InputStreamReader(new URL("http://"+connection.serverUrl.getHost()+"/josm/bookmarks").openStream()));
     162            bookmarks = new LinkedList<Bookmark>();
     163            for (String line = in.readLine(); line != null; line = in.readLine()) {
     164                StringTokenizer st = new StringTokenizer(line, ",");
     165                if (st.countTokens() < 5)
     166                    continue;
     167                Bookmark b = new Bookmark();
     168                b.name = st.nextToken();
     169                try {
     170                    for (int i = 0; i < b.latlon.length; ++i)
     171                        b.latlon[i] = Double.parseDouble(st.nextToken());
     172                    bookmarks.add(b);
     173                } catch (NumberFormatException x) {
     174                    // line not parsed
     175                }
     176            }
     177            in.close();
     178            return bookmarks;
     179        } catch (MalformedURLException e) {
     180            e.printStackTrace();
     181        } catch (IllegalArgumentException e) {
     182            e.printStackTrace();
     183        } catch (IOException e) {
     184            e.printStackTrace();
     185        }
     186        return Collections.emptyList();
     187    }
    188188
    189         @Override public void saveBookmarks(Collection<Bookmark> bookmarks) {
    190         }
     189    @Override public void saveBookmarks(Collection<Bookmark> bookmarks) {
     190    }
    191191}
  • trunk/src/org/openstreetmap/josm/data/UndoRedoHandler.java

    r661 r1169  
    1515public class UndoRedoHandler implements LayerChangeListener {
    1616
    17         /**
    18         * All commands that were made on the dataset. Don't write from outside!
    19         */
    20         public final LinkedList<Command> commands = new LinkedList<Command>();
    21         /**
    22         * The stack for redoing commands
    23         */
    24         private final Stack<Command> redoCommands = new Stack<Command>();
     17    /**
     18    * All commands that were made on the dataset. Don't write from outside!
     19    */
     20    public final LinkedList<Command> commands = new LinkedList<Command>();
     21    /**
     22    * The stack for redoing commands
     23    */
     24    private final Stack<Command> redoCommands = new Stack<Command>();
    2525
    26         public final LinkedList<CommandQueueListener> listenerCommands = new LinkedList<CommandQueueListener>();
     26    public final LinkedList<CommandQueueListener> listenerCommands = new LinkedList<CommandQueueListener>();
    2727
    2828
    29         public UndoRedoHandler() {
    30                 Layer.listeners.add(this);
    31         }
     29    public UndoRedoHandler() {
     30        Layer.listeners.add(this);
     31    }
    3232
    3333
    34         /**
    35         * Execute the command and add it to the intern command queue.
    36         */
    37         public void add(final Command c) {
    38                 c.executeCommand();
    39                 commands.add(c);
    40                 redoCommands.clear();
    41                 if (Main.map != null && Main.map.mapView.getActiveLayer() instanceof OsmDataLayer) {
    42                         OsmDataLayer data = (OsmDataLayer)Main.map.mapView.getActiveLayer();
    43                         data.setModified(true);
    44                         data.fireDataChange();
    45                 }
    46                 fireCommandsChanged();
    47         }
     34    /**
     35    * Execute the command and add it to the intern command queue.
     36    */
     37    public void add(final Command c) {
     38        c.executeCommand();
     39        commands.add(c);
     40        redoCommands.clear();
     41        if (Main.map != null && Main.map.mapView.getActiveLayer() instanceof OsmDataLayer) {
     42            OsmDataLayer data = (OsmDataLayer)Main.map.mapView.getActiveLayer();
     43            data.setModified(true);
     44            data.fireDataChange();
     45        }
     46        fireCommandsChanged();
     47    }
    4848
    49         /**
    50         * Undoes the last added command.
    51         */
    52         public void undo() {
    53                 if (commands.isEmpty())
    54                         return;
    55                 final Command c = commands.removeLast();
    56                 c.undoCommand();
    57                 redoCommands.push(c);
    58                 if (Main.map != null && Main.map.mapView.getActiveLayer() instanceof OsmDataLayer) {
    59                         OsmDataLayer data = (OsmDataLayer)Main.map.mapView.getActiveLayer();
    60                         data.setModified(data.uploadedModified || !commands.isEmpty());
    61                         data.fireDataChange();
    62                 }               
    63                 fireCommandsChanged();
    64                 Main.ds.setSelected();
    65         }
     49    /**
     50    * Undoes the last added command.
     51    */
     52    public void undo() {
     53        if (commands.isEmpty())
     54            return;
     55        final Command c = commands.removeLast();
     56        c.undoCommand();
     57        redoCommands.push(c);
     58        if (Main.map != null && Main.map.mapView.getActiveLayer() instanceof OsmDataLayer) {
     59            OsmDataLayer data = (OsmDataLayer)Main.map.mapView.getActiveLayer();
     60            data.setModified(data.uploadedModified || !commands.isEmpty());
     61            data.fireDataChange();
     62        }
     63        fireCommandsChanged();
     64        Main.ds.setSelected();
     65    }
    6666
    67         /**
    68         * Redoes the last undoed command.
    69         * TODO: This has to be moved to a central place in order to support multiple layers.
    70         */
    71         public void redo() {
    72                 if (redoCommands.isEmpty())
    73                         return;
    74                 final Command c = redoCommands.pop();
    75                 c.executeCommand();
    76                 commands.add(c);
    77                 if (Main.map != null && Main.map.mapView.getActiveLayer() instanceof OsmDataLayer) {
    78                         OsmDataLayer data = (OsmDataLayer)Main.map.mapView.getActiveLayer();
    79                         data.setModified(true);
    80                         data.fireDataChange();
    81                 }
    82                 fireCommandsChanged();
    83         }
     67    /**
     68    * Redoes the last undoed command.
     69    * TODO: This has to be moved to a central place in order to support multiple layers.
     70    */
     71    public void redo() {
     72        if (redoCommands.isEmpty())
     73            return;
     74        final Command c = redoCommands.pop();
     75        c.executeCommand();
     76        commands.add(c);
     77        if (Main.map != null && Main.map.mapView.getActiveLayer() instanceof OsmDataLayer) {
     78            OsmDataLayer data = (OsmDataLayer)Main.map.mapView.getActiveLayer();
     79            data.setModified(true);
     80            data.fireDataChange();
     81        }
     82        fireCommandsChanged();
     83    }
    8484
    85         public void fireCommandsChanged() {
    86                 for (final CommandQueueListener l : listenerCommands)
    87                         l.commandChanged(commands.size(), redoCommands.size());
    88         }
     85    public void fireCommandsChanged() {
     86        for (final CommandQueueListener l : listenerCommands)
     87            l.commandChanged(commands.size(), redoCommands.size());
     88    }
    8989
    90         public void clean() {
    91                 redoCommands.clear();
    92                 commands.clear();
    93                 fireCommandsChanged();
    94         }
     90    public void clean() {
     91        redoCommands.clear();
     92        commands.clear();
     93        fireCommandsChanged();
     94    }
    9595
    96         public void layerRemoved(Layer oldLayer) {
    97                 boolean changed = false;
    98                 for (Iterator<Command> it = commands.iterator(); it.hasNext();) {
    99                         if (it.next().invalidBecauselayerRemoved(oldLayer)) {
    100                                 it.remove();
    101                                 changed = true;
    102                         }
    103                 }
    104                 for (Iterator<Command> it = redoCommands.iterator(); it.hasNext();) {
    105                         if (it.next().invalidBecauselayerRemoved(oldLayer)) {
    106                                 it.remove();
    107                                 changed = true;
    108                         }
    109                 }
    110                 if (changed)
    111                         fireCommandsChanged();
    112         }
     96    public void layerRemoved(Layer oldLayer) {
     97        boolean changed = false;
     98        for (Iterator<Command> it = commands.iterator(); it.hasNext();) {
     99            if (it.next().invalidBecauselayerRemoved(oldLayer)) {
     100                it.remove();
     101                changed = true;
     102            }
     103        }
     104        for (Iterator<Command> it = redoCommands.iterator(); it.hasNext();) {
     105            if (it.next().invalidBecauselayerRemoved(oldLayer)) {
     106                it.remove();
     107                changed = true;
     108            }
     109        }
     110        if (changed)
     111            fireCommandsChanged();
     112    }
    113113
    114         public void layerAdded(Layer newLayer) {}
    115         public void activeLayerChange(Layer oldLayer, Layer newLayer) {}
     114    public void layerAdded(Layer newLayer) {}
     115    public void activeLayerChange(Layer oldLayer, Layer newLayer) {}
    116116}
  • trunk/src/org/openstreetmap/josm/data/conflict/ConflictItem.java

    r627 r1169  
    1212public abstract class ConflictItem {
    1313
    14         public String my, their;
    15         public Resolution resolution = null;
     14    public String my, their;
     15    public Resolution resolution = null;
    1616
    17         public final void initialize(Map<OsmPrimitive,OsmPrimitive> conflicts) {
    18                 my = collectStr(conflicts.keySet());
    19                 their = collectStr(conflicts.values());
    20         }
     17    public final void initialize(Map<OsmPrimitive,OsmPrimitive> conflicts) {
     18        my = collectStr(conflicts.keySet());
     19        their = collectStr(conflicts.values());
     20    }
    2121
    22         abstract public boolean hasConflict(OsmPrimitive key, OsmPrimitive value);
    23         abstract protected String str(OsmPrimitive osm);
    24         abstract public String key();
    25         abstract public void apply(OsmPrimitive target, OsmPrimitive other);
     22    abstract public boolean hasConflict(OsmPrimitive key, OsmPrimitive value);
     23    abstract protected String str(OsmPrimitive osm);
     24    abstract public String key();
     25    abstract public void apply(OsmPrimitive target, OsmPrimitive other);
    2626
    27         protected final String collectStr(Collection<OsmPrimitive> c) {
    28                 String value = null;
    29                 for (OsmPrimitive osm : c) {
    30                         String v = str(osm);
    31                         if (value == null)
    32                                 value = v;
    33                         else if (!value.equals(v)) {
    34                                 value = "<html><i>&lt;"+tr("different")+"&gt;</i></html>";
    35                                 break;
    36                         }
    37                 }
    38                 return value == null ? "" : value;
    39         }
     27    protected final String collectStr(Collection<OsmPrimitive> c) {
     28        String value = null;
     29        for (OsmPrimitive osm : c) {
     30            String v = str(osm);
     31            if (value == null)
     32                value = v;
     33            else if (!value.equals(v)) {
     34                value = "<html><i>&lt;"+tr("different")+"&gt;</i></html>";
     35                break;
     36            }
     37        }
     38        return value == null ? "" : value;
     39    }
    4040}
  • trunk/src/org/openstreetmap/josm/data/conflict/DeleteConflict.java

    r627 r1169  
    88public class DeleteConflict extends ConflictItem {
    99
    10         @Override public boolean hasConflict(OsmPrimitive key, OsmPrimitive value) {
    11                 return key.deleted != value.deleted;
    12         }
     10    @Override public boolean hasConflict(OsmPrimitive key, OsmPrimitive value) {
     11        return key.deleted != value.deleted;
     12    }
    1313
    14         @Override public String key() {
    15                 return "deleted|"+tr("deleted");
    16         }
     14    @Override public String key() {
     15        return "deleted|"+tr("deleted");
     16    }
    1717
    18         @Override protected String str(OsmPrimitive osm) {
    19                 return osm.deleted ? tr("true") : tr("false");
    20         }
     18    @Override protected String str(OsmPrimitive osm) {
     19        return osm.deleted ? tr("true") : tr("false");
     20    }
    2121
    22         @Override public void apply(OsmPrimitive target, OsmPrimitive other) {
    23                 target.deleted = other.deleted;
    24         }
     22    @Override public void apply(OsmPrimitive target, OsmPrimitive other) {
     23        target.deleted = other.deleted;
     24    }
    2525}
  • trunk/src/org/openstreetmap/josm/data/conflict/PositionConflict.java

    r627 r1169  
    88
    99public class PositionConflict extends ConflictItem {
    10        
    11         @Override public boolean hasConflict(OsmPrimitive key, OsmPrimitive value) {
    12                 return key instanceof Node && !((Node)key).coor.equals(((Node)value).coor);
    13         }
    14        
    15         @Override protected String str(OsmPrimitive osm) {
    16                 return osm instanceof Node ? ((Node)osm).coor.lat()+", "+((Node)osm).coor.lon() : null;
    17         }
    18        
    19         @Override public String key() {
    20                 return "node|"+tr("position");
    21         }
    22        
    23         @Override public void apply(OsmPrimitive target, OsmPrimitive other) {
    24                 if (target instanceof Node) {
    25                         ((Node)target).coor = ((Node)other).coor;
    26                         ((Node)target).eastNorth = ((Node)other).eastNorth;
    27                 }
     10
     11    @Override public boolean hasConflict(OsmPrimitive key, OsmPrimitive value) {
     12        return key instanceof Node && !((Node)key).coor.equals(((Node)value).coor);
     13    }
     14
     15    @Override protected String str(OsmPrimitive osm) {
     16        return osm instanceof Node ? ((Node)osm).coor.lat()+", "+((Node)osm).coor.lon() : null;
     17    }
     18
     19    @Override public String key() {
     20        return "node|"+tr("position");
     21    }
     22
     23    @Override public void apply(OsmPrimitive target, OsmPrimitive other) {
     24        if (target instanceof Node) {
     25            ((Node)target).coor = ((Node)other).coor;
     26            ((Node)target).eastNorth = ((Node)other).eastNorth;
     27        }
    2828    }
    2929}
  • trunk/src/org/openstreetmap/josm/data/conflict/PropertyConflict.java

    r627 r1169  
    55
    66public class PropertyConflict extends ConflictItem {
    7         private String key;
    8        
    9         public PropertyConflict(String key) {
    10                 this.key = key;
    11         }
    12        
    13         @Override public boolean hasConflict(OsmPrimitive key, OsmPrimitive value) {
    14                 String k = key.get(this.key);
    15                 String v = value.get(this.key);
    16                 return k == null ? v!=null : !k.equals(v);
    17         }
    18        
    19         @Override protected String str(OsmPrimitive osm) {
    20                 String v = osm.get(key);
    21                 return v == null ? "" : v;
    22         }
    23        
    24         @Override public String key() {
    25                 return "key|"+key;
    26         }
    27        
    28         @Override public void apply(OsmPrimitive target, OsmPrimitive other) {
    29                 target.put(key, other.get(key));
     7    private String key;
     8
     9    public PropertyConflict(String key) {
     10        this.key = key;
     11    }
     12
     13    @Override public boolean hasConflict(OsmPrimitive key, OsmPrimitive value) {
     14        String k = key.get(this.key);
     15        String v = value.get(this.key);
     16        return k == null ? v!=null : !k.equals(v);
     17    }
     18
     19    @Override protected String str(OsmPrimitive osm) {
     20        String v = osm.get(key);
     21        return v == null ? "" : v;
     22    }
     23
     24    @Override public String key() {
     25        return "key|"+key;
     26    }
     27
     28    @Override public void apply(OsmPrimitive target, OsmPrimitive other) {
     29        target.put(key, other.get(key));
    3030    }
    3131}
  • trunk/src/org/openstreetmap/josm/data/coor/Coordinate.java

    r1094 r1169  
    77/**
    88 * Base class of points of both coordinate systems.
    9  * 
    10  * The variables are default package protected to allow routines in the 
     9 *
     10 * The variables are default package protected to allow routines in the
    1111 * data package to access them directly.
    12  * 
    13  * As the class itself is package protected too, it is not visible 
    14  * outside of the data package. Routines there should only use LatLon or 
     12 *
     13 * As the class itself is package protected too, it is not visible
     14 * outside of the data package. Routines there should only use LatLon or
    1515 * EastNorth.
    1616 *
    1717 * @author imi
    18  */ 
     18 */
    1919abstract class Coordinate extends Point2D implements Serializable {
    2020
    2121    protected double x;
    2222    protected double y;
    23    
    24         /**
    25         * Construct the point with latitude / longitude values.
    26          *
    27         * @param x X coordinate of the point.
    28         * @param y Y coordinate of the point.
    29         */
    30         Coordinate(double x, double y) {
     23
     24    /**
     25    * Construct the point with latitude / longitude values.
     26     *
     27    * @param x X coordinate of the point.
     28    * @param y Y coordinate of the point.
     29    */
     30    Coordinate(double x, double y) {
    3131        this.x = x; this.y = y;
    32         }
    33    
     32    }
     33
    3434    public double getX() {
    3535        return x;
    3636    }
    37    
     37
    3838    public double getY() {
    39         return y; 
     39        return y;
    4040    }
    41    
     41
    4242    public void setLocation (double x, double y) {
    43         this.x = x; 
     43        this.x = x;
    4444        this.y = y;
    4545    }
  • trunk/src/org/openstreetmap/josm/data/coor/EastNorth.java

    r1076 r1169  
    44/**
    55 * Northing, Easting of the projected coordinates.
    6  * 
     6 *
    77 * This class is immutable.
    8  * 
     8 *
    99 * @author Imi
    1010 */
    1111public class EastNorth extends Coordinate {
    1212
    13         public EastNorth(double east, double north) {
    14                 super(east,north);
    15         }
    16        
    17         public double east() {
    18                 return x;
    19         }
     13    public EastNorth(double east, double north) {
     14        super(east,north);
     15    }
    2016
    21         public double north() {
    22                 return y;
    23         }
     17    public double east() {
     18        return x;
     19    }
    2420
    25         public EastNorth add(double dx, double dy) {
    26                 return new EastNorth(x+dx, y+dy);
    27         }
    28        
    29         public EastNorth interpolate(EastNorth en2, double proportion) {
    30                 return new EastNorth(this.x + proportion * (en2.x - this.x),
     21    public double north() {
     22        return y;
     23    }
     24
     25    public EastNorth add(double dx, double dy) {
     26        return new EastNorth(x+dx, y+dy);
     27    }
     28
     29    public EastNorth interpolate(EastNorth en2, double proportion) {
     30        return new EastNorth(this.x + proportion * (en2.x - this.x),
    3131            this.y + proportion * (en2.y - this.y));
    32         }
    33    
     32    }
     33
    3434    /**
    35      * Returns the heading, in radians, that you have to use to get from 
     35     * Returns the heading, in radians, that you have to use to get from
    3636     * this EastNorth to another. Heading is mapped into [0, 2pi)
    37      * 
     37     *
    3838     * @param other the "destination" position
    39      * @return heading 
     39     * @return heading
    4040     */
    4141    public double heading(EastNorth other) {
    4242        double hd = Math.atan2(other.east() - east(), other.north() - north());
    4343        if(hd < 0) hd = 2 * Math.PI + hd;
    44         return hd;       
     44        return hd;
    4545    }
    46    
     46
    4747    public EastNorth sub(EastNorth en) {
    4848        return new EastNorth(en.east() - east(), en.north() - north());
    4949    }
    50  
     50
    5151    /**
    5252     * Returns an EastNorth representing the this EastNorth rotatedaround
     
    5454     * @param pivot the center of the rotation
    5555     * @param angle the angle of the rotation
    56      * @return EastNorth rotated object 
     56     * @return EastNorth rotated object
    5757     */
    5858    public EastNorth rotate(EastNorth pivot, double angle) {
     
    6565        return new EastNorth(nx, ny);
    6666    }
    67        
    68         @Override public String toString() {
    69                 return "EastNorth[e="+x+", n="+y+"]";
    70         }
     67
     68    @Override public String toString() {
     69        return "EastNorth[e="+x+", n="+y+"]";
     70    }
    7171
    7272    /**
  • trunk/src/org/openstreetmap/josm/data/coor/LatLon.java

    r1108 r1169  
    1313/**
    1414 * LatLon are unprojected latitude / longitude coordinates.
    15  * 
     15 *
    1616 * This class is immutable.
    17  * 
     17 *
    1818 * @author Imi
    1919 */
     
    2323    private static DecimalFormat cDmsSecondFormatter = new DecimalFormat("00.0");
    2424    private static DecimalFormat cDdFormatter = new DecimalFormat("###0.0000");
    25    
    26     /** 
    27      * Possible ways to display coordinates 
    28      */ 
    29     public enum CoordinateFormat { 
    30         DECIMAL_DEGREES {public String toString() {return tr("Decimal Degrees");}}, 
    31         DEGREES_MINUTES_SECONDS {public String toString() {return tr("Degrees Minutes Seconds");}}; 
    32     } 
    33    
    34     public static String dms(double pCoordinate) { 
     25
     26    /**
     27     * Possible ways to display coordinates
     28     */
     29    public enum CoordinateFormat {
     30        DECIMAL_DEGREES {public String toString() {return tr("Decimal Degrees");}},
     31        DEGREES_MINUTES_SECONDS {public String toString() {return tr("Degrees Minutes Seconds");}};
     32    }
     33
     34    public static String dms(double pCoordinate) {
    3535
    3636        double tAbsCoord = Math.abs(pCoordinate);
     
    4040        double tSeconds = (tTmpMinutes - tMinutes) * 60;
    4141
    42         return tDegree + "\u00B0" + cDmsMinuteFormatter.format(tMinutes) + "\'" 
     42        return tDegree + "\u00B0" + cDmsMinuteFormatter.format(tMinutes) + "\'"
    4343            + cDmsSecondFormatter.format(tSeconds) + "\"";
    44     } 
     44    }
    4545
    4646    public LatLon(double lat, double lon) {
     
    5151        return y;
    5252    }
    53    
     53
    5454    public String latToString(CoordinateFormat d) {
    5555        switch(d) {
     
    5959        }
    6060    }
    61    
     61
    6262    public double lon() {
    6363        return x;
    6464    }
    65    
     65
    6666    public String lonToString(CoordinateFormat d) {
    6767        switch(d) {
     
    7171        }
    7272    }
    73    
    74  
     73
     74
    7575
    7676    /**
     
    8686    /**
    8787     * @return <code>true</code>, if the coordinate is outside the world, compared
    88         * by using lat/lon.
    89         */
    90         public boolean isOutSideWorld() {
    91                 return lat() < -Projection.MAX_LAT || lat() > Projection.MAX_LAT ||
    92                         lon() < -Projection.MAX_LON || lon() > Projection.MAX_LON;
    93         }
     88    * by using lat/lon.
     89    */
     90    public boolean isOutSideWorld() {
     91        return lat() < -Projection.MAX_LAT || lat() > Projection.MAX_LAT ||
     92            lon() < -Projection.MAX_LON || lon() > Projection.MAX_LON;
     93    }
    9494
    95         /**
    96          * @return <code>true</code> if this is within the given bounding box.
    97          */
    98         public boolean isWithin(Bounds b) {
    99                 return lat() >= b.min.lat() && lat() <= b.max.lat() && lon() > b.min.lon() && lon() < b.max.lon();
    100         }
    101        
    102         /**
    103          * Computes the distance between this lat/lon and another point on the earth.
    104          * Uses spherical law of cosines formula, not Haversine.
    105          * @param other the other point.
    106          * @return distance in metres.
    107          */
    108         public double greatCircleDistance(LatLon other) {
    109                 return (Math.acos(
    110                         Math.sin(Math.toRadians(lat())) * Math.sin(Math.toRadians(other.lat())) +
    111                     Math.cos(Math.toRadians(lat()))*Math.cos(Math.toRadians(other.lat())) *
    112                                   Math.cos(Math.toRadians(other.lon()-lon()))) * 6378135);
    113         }
    114        
    115         /**
    116          * Returns the heading, in radians, that you have to use to get from
    117          * this lat/lon to another.
    118          *
    119          * @param other the "destination" position
    120          * @return heading
    121          */
    122         public double heading(LatLon other) {
    123                 double rv;
    124                 if (other.lat() == lat()) {
    125                         rv = (other.lon()>lon() ? Math.PI / 2 : Math.PI * 3 / 2);
    126                 } else {
    127                         rv = Math.atan((other.lon()-lon())/(other.lat()-lat()));
    128                         if (rv < 0) rv += Math.PI;
    129                         if (other.lon() < lon()) rv += Math.PI;
    130                 }
    131                 return rv;
    132         }
     95    /**
     96     * @return <code>true</code> if this is within the given bounding box.
     97     */
     98    public boolean isWithin(Bounds b) {
     99        return lat() >= b.min.lat() && lat() <= b.max.lat() && lon() > b.min.lon() && lon() < b.max.lon();
     100    }
    133101
    134         /**
    135          * Returns this lat/lon pair in human-readable format.
    136          *
    137          * @return String in the format "lat=1.23456°, lon=2.34567°"
    138          */
    139         public String toDisplayString() {
    140                 NumberFormat nf = NumberFormat.getInstance();
    141                 nf.setMaximumFractionDigits(5);
    142                 return "lat=" + nf.format(lat()) + "°, lon=" + nf.format(lon()) + "°";
    143         }
    144        
    145         @Override public String toString() {
    146                 return "LatLon[lat="+lat()+",lon="+lon()+"]";
     102    /**
     103     * Computes the distance between this lat/lon and another point on the earth.
     104     * Uses spherical law of cosines formula, not Haversine.
     105     * @param other the other point.
     106     * @return distance in metres.
     107     */
     108    public double greatCircleDistance(LatLon other) {
     109        return (Math.acos(
     110            Math.sin(Math.toRadians(lat())) * Math.sin(Math.toRadians(other.lat())) +
     111            Math.cos(Math.toRadians(lat()))*Math.cos(Math.toRadians(other.lat())) *
     112                          Math.cos(Math.toRadians(other.lon()-lon()))) * 6378135);
     113    }
     114
     115    /**
     116     * Returns the heading, in radians, that you have to use to get from
     117     * this lat/lon to another.
     118     *
     119     * @param other the "destination" position
     120     * @return heading
     121     */
     122    public double heading(LatLon other) {
     123        double rv;
     124        if (other.lat() == lat()) {
     125            rv = (other.lon()>lon() ? Math.PI / 2 : Math.PI * 3 / 2);
     126        } else {
     127            rv = Math.atan((other.lon()-lon())/(other.lat()-lat()));
     128            if (rv < 0) rv += Math.PI;
     129            if (other.lon() < lon()) rv += Math.PI;
     130        }
     131        return rv;
     132    }
     133
     134    /**
     135     * Returns this lat/lon pair in human-readable format.
     136     *
     137     * @return String in the format "lat=1.23456°, lon=2.34567°"
     138     */
     139    public String toDisplayString() {
     140        NumberFormat nf = NumberFormat.getInstance();
     141        nf.setMaximumFractionDigits(5);
     142        return "lat=" + nf.format(lat()) + "°, lon=" + nf.format(lon()) + "°";
     143    }
     144
     145    @Override public String toString() {
     146        return "LatLon[lat="+lat()+",lon="+lon()+"]";
    147147    }
    148148}
  • trunk/src/org/openstreetmap/josm/data/gpx/GpxData.java

    r655 r1169  
    1515 * It uses GPX v1.1, see {@link <a href="http://www.topografix.com/GPX/1/1/">the spec</a>}
    1616 * for details.
    17  * 
     17 *
    1818 * @author Raphael Mack <ramack@raphael-mack.de>
    1919 */
    2020public class GpxData extends WithAttributes {
    21         public File storageFile;
    22         public boolean fromServer;
     21    public File storageFile;
     22    public boolean fromServer;
    2323
    24         public Collection<GpxTrack> tracks = new LinkedList<GpxTrack>();
    25         public Collection<GpxRoute> routes = new LinkedList<GpxRoute>();
    26         public Collection<WayPoint> waypoints = new LinkedList<WayPoint>();
     24    public Collection<GpxTrack> tracks = new LinkedList<GpxTrack>();
     25    public Collection<GpxRoute> routes = new LinkedList<GpxRoute>();
     26    public Collection<WayPoint> waypoints = new LinkedList<WayPoint>();
    2727
    28         public Bounds bounds;
     28    public Bounds bounds;
    2929
    30         public void mergeFrom(GpxData other) {
    31                 if (storageFile == null && other.storageFile != null) {
    32                         storageFile = other.storageFile;
    33                 }
    34                 fromServer = fromServer && other.fromServer;
     30    public void mergeFrom(GpxData other) {
     31        if (storageFile == null && other.storageFile != null) {
     32            storageFile = other.storageFile;
     33        }
     34        fromServer = fromServer && other.fromServer;
    3535
    36                 for (Map.Entry<String, Object> ent : other.attr.entrySet()) {
    37                         // TODO: Detect conflicts.
    38                         String k = ent.getKey();
    39                         if (k.equals("link") && attr.containsKey("link")) {
    40                                 ((Collection<GpxLink>) attr.get("link")).addAll(
    41                                         (Collection<GpxLink>) ent.getValue());
    42                         } else {
    43                                 attr.put(k, ent.getValue());
    44                         }
    45                 }
    46                 tracks.addAll(other.tracks);
    47                 routes.addAll(other.routes);
    48                 waypoints.addAll(other.waypoints);
    49         }
     36        for (Map.Entry<String, Object> ent : other.attr.entrySet()) {
     37            // TODO: Detect conflicts.
     38            String k = ent.getKey();
     39            if (k.equals("link") && attr.containsKey("link")) {
     40                ((Collection<GpxLink>) attr.get("link")).addAll(
     41                    (Collection<GpxLink>) ent.getValue());
     42            } else {
     43                attr.put(k, ent.getValue());
     44            }
     45        }
     46        tracks.addAll(other.tracks);
     47        routes.addAll(other.routes);
     48        waypoints.addAll(other.waypoints);
     49    }
    5050
    51         public boolean hasTrackPoints() {
    52                 for (GpxTrack trk : tracks) {
    53                         for (Collection<WayPoint> trkseg : trk.trackSegs) {
    54                                 if (!trkseg.isEmpty())
    55                                         return true;
    56                         }
    57                 }
    58                 return false;
    59         }
     51    public boolean hasTrackPoints() {
     52        for (GpxTrack trk : tracks) {
     53            for (Collection<WayPoint> trkseg : trk.trackSegs) {
     54                if (!trkseg.isEmpty())
     55                    return true;
     56            }
     57        }
     58        return false;
     59    }
    6060
    61         public boolean hasRoutePoints() {
    62                 for (GpxRoute rte : routes) {
    63                         if (!rte.routePoints.isEmpty())
    64                                 return true;
    65                 }
    66                 return false;
    67         }
     61    public boolean hasRoutePoints() {
     62        for (GpxRoute rte : routes) {
     63            if (!rte.routePoints.isEmpty())
     64                return true;
     65        }
     66        return false;
     67    }
    6868
    69         // FIXME might perhaps use visitor pattern?
    70         public void recalculateBounds() {
    71                 bounds = null;
    72                 for (WayPoint wpt : waypoints) {
    73                         if (bounds == null) {
    74                                 bounds = new Bounds(wpt.latlon, wpt.latlon);
    75                         } else {
    76                                 bounds.extend(wpt.latlon);
    77                         }
    78                 }
    79                 for (GpxRoute rte : routes) {
    80                         for (WayPoint wpt : rte.routePoints) {
    81                                 if (bounds == null) {
    82                                         bounds = new Bounds(wpt.latlon, wpt.latlon);
    83                                 } else {
    84                                         bounds.extend(wpt.latlon);
    85                                 }
    86                         }
    87                 }
    88                 for (GpxTrack trk : tracks) {
    89                         for (Collection<WayPoint> trkseg : trk.trackSegs) {
    90                                 for (WayPoint wpt : trkseg) {
    91                                         if (bounds == null) {
    92                                                 bounds = new Bounds(wpt.latlon, wpt.latlon);
    93                                         } else {
    94                                                 bounds.extend(wpt.latlon);
    95                                         }
    96                                 }
    97                         }
    98                 }
    99                 if (bounds == null) {
    100                         bounds = new Bounds();
    101                 }
    102         }
    103    
     69    // FIXME might perhaps use visitor pattern?
     70    public void recalculateBounds() {
     71        bounds = null;
     72        for (WayPoint wpt : waypoints) {
     73            if (bounds == null) {
     74                bounds = new Bounds(wpt.latlon, wpt.latlon);
     75            } else {
     76                bounds.extend(wpt.latlon);
     77            }
     78        }
     79        for (GpxRoute rte : routes) {
     80            for (WayPoint wpt : rte.routePoints) {
     81                if (bounds == null) {
     82                    bounds = new Bounds(wpt.latlon, wpt.latlon);
     83                } else {
     84                    bounds.extend(wpt.latlon);
     85                }
     86            }
     87        }
     88        for (GpxTrack trk : tracks) {
     89            for (Collection<WayPoint> trkseg : trk.trackSegs) {
     90                for (WayPoint wpt : trkseg) {
     91                    if (bounds == null) {
     92                        bounds = new Bounds(wpt.latlon, wpt.latlon);
     93                    } else {
     94                        bounds.extend(wpt.latlon);
     95                    }
     96                }
     97            }
     98        }
     99        if (bounds == null) {
     100            bounds = new Bounds();
     101        }
     102    }
     103
    104104    /**
    105105     * calculates the sum of the lengths of all track segments
     
    108108        double result = 0.0; // in meters
    109109        WayPoint last = null;
    110                
     110
    111111        for (GpxTrack trk : tracks) {
    112112            for (Collection<WayPoint> trkseg : trk.trackSegs) {
     
    129129        double lat1, lon1, lat2, lon2;
    130130        double dlon, dlat;
    131            
     131
    132132        lat1 = p1.lat() * Math.PI / 180.0;
    133133        lon1 = p1.lon() * Math.PI / 180.0;
  • trunk/src/org/openstreetmap/josm/data/gpx/GpxLink.java

    r627 r1169  
    55
    66public class GpxLink {
    7         public String uri;
    8         public String text;
    9         public String type;
     7    public String uri;
     8    public String text;
     9    public String type;
    1010
    11         public GpxLink(String uri) {
    12                 this.uri = uri;
    13         }
     11    public GpxLink(String uri) {
     12        this.uri = uri;
     13    }
    1414}
  • trunk/src/org/openstreetmap/josm/data/gpx/GpxRoute.java

    r627 r1169  
    88
    99public class GpxRoute extends WithAttributes {
    10         public Collection<WayPoint> routePoints = new LinkedList<WayPoint>();
     10    public Collection<WayPoint> routePoints = new LinkedList<WayPoint>();
    1111}
  • trunk/src/org/openstreetmap/josm/data/gpx/GpxTrack.java

    r627 r1169  
    88
    99public class GpxTrack extends WithAttributes {
    10         public Collection<Collection<WayPoint>> trackSegs
    11                 = new LinkedList<Collection<WayPoint>>();
     10    public Collection<Collection<WayPoint>> trackSegs
     11        = new LinkedList<Collection<WayPoint>>();
    1212}
  • trunk/src/org/openstreetmap/josm/data/gpx/WayPoint.java

    r1167 r1169  
    1515public class WayPoint extends WithAttributes implements Comparable<WayPoint>
    1616{
    17         public final LatLon latlon;
    18         public final EastNorth eastNorth;
    19         public double time;
    20         public Color speedLineColor;
    21         public boolean drawLine;
    22         public int dir;
     17    public final LatLon latlon;
     18    public final EastNorth eastNorth;
     19    public double time;
     20    public Color speedLineColor;
     21    public boolean drawLine;
     22    public int dir;
    2323
    24         public WayPoint(LatLon ll) {
    25                 latlon = ll;
    26                 eastNorth = Main.proj.latlon2eastNorth(ll);
    27         }
     24    public WayPoint(LatLon ll) {
     25        latlon = ll;
     26        eastNorth = Main.proj.latlon2eastNorth(ll);
     27    }
    2828
    29         @Override
    30         public String toString() {
    31                 return "WayPoint (" + (attr.containsKey("name") ? attr.get("name") + ", " :"") + latlon.toString() + ", " + attr + ")";
    32         }
    33        
    34         /**
    35          * Convert the time stamp of the waypoint into seconds from the epoch
    36          */
    37         public final static SimpleDateFormat GPXTIMEFMT =
    38                 new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS"); // ignore timezone
    39        
    40         public void setTime() {
    41                 if (! attr.containsKey("time")) {
    42                         return;
    43                 }
    44                 Date d = GPXTIMEFMT.parse(attr.get("time").toString(), new ParsePosition(0));
    45                 if (d != null /* parsing ok */) {
    46                         time = d.getTime() / 1000.0; /* ms => seconds */
    47                 }
    48         }
     29    @Override
     30    public String toString() {
     31        return "WayPoint (" + (attr.containsKey("name") ? attr.get("name") + ", " :"") + latlon.toString() + ", " + attr + ")";
     32    }
    4933
    50         /**
    51          * Convert a time stamp of the waypoint from the <cmt> or <desc> field
    52          * into seconds from the epoch. Handles the date format as it is used by
    53          * Garmin handhelds. Does not overwrite an existing timestamp (!= 0.0).
    54          * A value of <time> fields overwrites values set with by method.
    55          * Does nothing if specified key does not exist or text cannot be parsed.
    56          *
    57          * @param key The key that contains the text to convert.
    58          */
    59         public void setGarminCommentTime(String key) {
    60                 // do not overwrite time if already set
    61                 if (time != 0.0) {
    62                         return;
    63                 }
    64                 if (! attr.containsKey(key)) {
    65                         return;
    66                 }
    67                 // example date format "18-AUG-08 13:33:03"
    68                 SimpleDateFormat f = new SimpleDateFormat("dd-MMM-yy HH:mm:ss"); // Garmin wpts have no timezone
    69                 Date d = f.parse(attr.get(key).toString(), new ParsePosition(0));
    70                 if (d != null /* parsing OK */) {
    71                         time = d.getTime() / 1000.0; /* ms => seconds */
    72                 }
    73         }
     34    /**
     35     * Convert the time stamp of the waypoint into seconds from the epoch
     36     */
     37    public final static SimpleDateFormat GPXTIMEFMT =
     38        new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS"); // ignore timezone
    7439
    75         public int compareTo(WayPoint w)
    76         {
    77                 return Double.compare(time, w.time);
    78         }
     40    public void setTime() {
     41        if (! attr.containsKey("time")) {
     42            return;
     43        }
     44        Date d = GPXTIMEFMT.parse(attr.get("time").toString(), new ParsePosition(0));
     45        if (d != null /* parsing ok */) {
     46            time = d.getTime() / 1000.0; /* ms => seconds */
     47        }
     48    }
     49
     50    /**
     51     * Convert a time stamp of the waypoint from the <cmt> or <desc> field
     52     * into seconds from the epoch. Handles the date format as it is used by
     53     * Garmin handhelds. Does not overwrite an existing timestamp (!= 0.0).
     54     * A value of <time> fields overwrites values set with by method.
     55     * Does nothing if specified key does not exist or text cannot be parsed.
     56     *
     57     * @param key The key that contains the text to convert.
     58     */
     59    public void setGarminCommentTime(String key) {
     60        // do not overwrite time if already set
     61        if (time != 0.0) {
     62            return;
     63        }
     64        if (! attr.containsKey(key)) {
     65            return;
     66        }
     67        // example date format "18-AUG-08 13:33:03"
     68        SimpleDateFormat f = new SimpleDateFormat("dd-MMM-yy HH:mm:ss"); // Garmin wpts have no timezone
     69        Date d = f.parse(attr.get(key).toString(), new ParsePosition(0));
     70        if (d != null /* parsing OK */) {
     71            time = d.getTime() / 1000.0; /* ms => seconds */
     72        }
     73    }
     74
     75    public int compareTo(WayPoint w)
     76    {
     77        return Double.compare(time, w.time);
     78    }
    7979}
  • trunk/src/org/openstreetmap/josm/data/gpx/WithAttributes.java

    r627 r1169  
    1 // License: GPL. 
     1// License: GPL.
    22package org.openstreetmap.josm.data.gpx;
    33
     
    99 * The "attr" hash is used to store the XML payload
    1010 * (not only XML attributes!)
    11  * 
     11 *
    1212 * @author Frederik Ramm <frederik@remote.org>
    1313 *
    1414 */
    1515public class WithAttributes {
    16        
    17         public Map<String, Object> attr = new HashMap<String, Object>(0);
    18        
    19         public String getString(String key) {
    20                 Object value = attr.get(key);
    21                 return (value instanceof String) ? (String)value : null;
    22         }
     16
     17    public Map<String, Object> attr = new HashMap<String, Object>(0);
     18
     19    public String getString(String key) {
     20        Object value = attr.get(key);
     21        return (value instanceof String) ? (String)value : null;
     22    }
    2323}
  • trunk/src/org/openstreetmap/josm/data/osm/Changeset.java

    r655 r1169  
    2020 */
    2121public final class Changeset /*extends OsmPrimitive*/ implements OsmWriterInterface {
    22         /**
    23         * The key/value list for this primitive.
    24         */
    25         public Map<String, String> keys;
     22    /**
     23    * The key/value list for this primitive.
     24    */
     25    public Map<String, String> keys;
    2626
    27         public long id = 0;
    28        
    29         /**
    30         * User that created this changeset, as specified by the server.
    31         * Never changed by JOSM.
    32         */
    33         public User user = null;
    34        
    35         /**
    36         * Time of last modification to this object. This is not set by JOSM but
    37          * read from the server and delivered back to the server unmodified.
    38         */
    39         public String end_timestamp = null;
    40        
    41         /**
    42         * Time of first modification to this object. This is not set by JOSM but
    43         * read from the server and delivered back to the server unmodified.
    44         */
    45         public String start_timestamp = null;
     27    public long id = 0;
     28
     29    /**
     30    * User that created this changeset, as specified by the server.
     31    * Never changed by JOSM.
     32    */
     33    public User user = null;
     34
     35    /**
     36    * Time of last modification to this object. This is not set by JOSM but
     37     * read from the server and delivered back to the server unmodified.
     38    */
     39    public String end_timestamp = null;
     40
     41    /**
     42    * Time of first modification to this object. This is not set by JOSM but
     43    * read from the server and delivered back to the server unmodified.
     44    */
     45    public String start_timestamp = null;
    4646
    4747        private void addTags(PrintWriter out) {
     
    5353        }
    5454
    55         public final void header(PrintWriter out) {
    56                 out.print("<osm version='");
     55    public final void header(PrintWriter out) {
     56            out.print("<osm version='");
    5757                out.print(Main.pref.get("osm-server.version", "0.6"));
    5858                out.println("' generator='JOSM'>");
    59         }
    60         public final void write(PrintWriter out) {
    61                 out.print("  <changeset");
    62                 if (id != 0)
    63                         out.print(" id="+id);
    64                 if (this.user != null) {
    65                         out.print(" user='"+XmlWriter.encode(this.user.name)+"'");
    66                 }
    67                 out.println(">\n");
    68                 addTags( out );
    69                 out.println("  </changeset>");
    70         }
    71         public final void footer(PrintWriter out) {
    72                 out.println("</osm>");
    73         }
    74        
    75         /******************************************************
    76          * This tag stuff is copied from OsmPrimitive. Perhaps a changeset
    77          * really is a primitive, but it's not right now. Perhaps it should
    78          * be...
    79          ******************************************************/
    80          
    81         /**
    82          * Set the given value to the given key
    83          * @param key The key, for which the value is to be set.
    84          * @param value The value for the key.
    85          */
    86         public final void put(String key, String value) {
    87                 if (value == null)
    88                         remove(key);
    89                 else {
    90                         if (keys == null)
    91                                 keys = new HashMap<String, String>();
    92                         keys.put(key, value);
    93                 }
    94         }
    95         /**
    96          * Remove the given key from the list.
    97          */
    98         public final void remove(String key) {
    99                 if (keys != null) {
    100                         keys.remove(key);
    101                         if (keys.isEmpty())
    102                                 keys = null;
    103                 }
    104         }
     59    }
     60    public final void write(PrintWriter out) {
     61        out.print("  <changeset");
     62        if (id != 0)
     63            out.print(" id="+id);
     64        if (this.user != null) {
     65            out.print(" user='"+XmlWriter.encode(this.user.name)+"'");
     66        }
     67        out.println(">\n");
     68        addTags( out );
     69        out.println("  </changeset>");
     70    }
     71    public final void footer(PrintWriter out) {
     72        out.println("</osm>");
     73    }
    10574
    106         public final String get(String key) {
    107                 return keys == null ? null : keys.get(key);
    108         }
     75    /******************************************************
     76     * This tag stuff is copied from OsmPrimitive. Perhaps a changeset
     77     * really is a primitive, but it's not right now. Perhaps it should
     78     * be...
     79     ******************************************************/
    10980
    110         public final Collection<Entry<String, String>> entrySet() {
    111                 if (keys == null)
    112                         return Collections.emptyList();
    113                 return keys.entrySet();
    114         }
     81    /**
     82     * Set the given value to the given key
     83     * @param key The key, for which the value is to be set.
     84     * @param value The value for the key.
     85     */
     86    public final void put(String key, String value) {
     87        if (value == null)
     88            remove(key);
     89        else {
     90            if (keys == null)
     91                keys = new HashMap<String, String>();
     92            keys.put(key, value);
     93        }
     94    }
     95    /**
     96     * Remove the given key from the list.
     97     */
     98    public final void remove(String key) {
     99        if (keys != null) {
     100            keys.remove(key);
     101            if (keys.isEmpty())
     102                keys = null;
     103        }
     104    }
    115105
    116         public final Collection<String> keySet() {
    117                 if (keys == null)
    118                         return Collections.emptyList();
    119                 return keys.keySet();
    120         }
     106    public final String get(String key) {
     107        return keys == null ? null : keys.get(key);
     108    }
     109
     110    public final Collection<Entry<String, String>> entrySet() {
     111        if (keys == null)
     112            return Collections.emptyList();
     113        return keys.entrySet();
     114    }
     115
     116    public final Collection<String> keySet() {
     117        if (keys == null)
     118            return Collections.emptyList();
     119        return keys.keySet();
     120    }
    121121}
  • trunk/src/org/openstreetmap/josm/data/osm/DataSet.java

    r1004 r1169  
    1717 * DataSet is the data behind the application. It can consists of only a few points up to the whole
    1818 * osm database. DataSet's can be merged together, saved, (up/down/disk)loaded etc.
    19  * 
     19 *
    2020 * Note that DataSet is not an osm-primitive and so has no key association but a few members to
    2121 * store some information.
    22  * 
     22 *
    2323 * @author imi
    2424 */
     
    3333    /**
    3434     * All ways (Streets etc.) in the DataSet.
    35      * 
     35     *
    3636     * The way nodes are stored only in the way list.
    3737     */
     
    235235
    236236    // Provide well-defined sorting for collections of OsmPrimitives.
    237     // FIXME: probably not a good place to put this code. 
     237    // FIXME: probably not a good place to put this code.
    238238    public static OsmPrimitive[] sort(Collection<? extends OsmPrimitive> list) {
    239239        OsmPrimitive[] selArr = new OsmPrimitive[list.size()];
  • trunk/src/org/openstreetmap/josm/data/osm/DataSource.java

    r627 r1169  
    55
    66public class DataSource implements Cloneable {
    7         public final Bounds bounds;
    8         public final String origin;
    9        
    10         public DataSource(Bounds bounds, String origin) {
    11             this.bounds = bounds;
    12             this.origin = origin;
     7    public final Bounds bounds;
     8    public final String origin;
     9
     10    public DataSource(Bounds bounds, String origin) {
     11        this.bounds = bounds;
     12        this.origin = origin;
    1313    }
    1414
    15         @Override protected Object clone() throws CloneNotSupportedException {
    16             return new DataSource(bounds, origin);
     15    @Override protected Object clone() throws CloneNotSupportedException {
     16        return new DataSource(bounds, origin);
    1717    }
    1818}
  • trunk/src/org/openstreetmap/josm/data/osm/DateFormatter.java

    r627 r1169  
    99/**
    1010 * Outputs a date in a format suitable for an OSM XML file.
    11  * 
     11 *
    1212 * @author Brett Henderson
    1313 */
    1414public class DateFormatter {
    15        
    16         private GregorianCalendar calendar;
    17        
    18        
    19         /**
    20         * Creates a new instance.
    21         */
    22         public DateFormatter() {
    23                 calendar = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
    24         }
    25        
    26        
    27         /**
    28         * Formats a date in XML format.
    29          *
    30         * @param date
    31         *            The date to be formatted.
    32         * @return The string representing the date.
    33         */
    34         public String format(Date date) {
    35                 StringBuilder result;
    36                 int year;
    37                 int month;
    38                 int day;
    39                 int hour;
    40                 int minute;
    41                 int second;
    42                
    43                 calendar.setTime(date);
    44                
    45                 result = new StringBuilder(20);
    46                
    47                 year = calendar.get(Calendar.YEAR);
    48                 month = calendar.get(Calendar.MONTH) + 1;
    49                 day = calendar.get(Calendar.DATE);
    50                 hour = calendar.get(Calendar.HOUR_OF_DAY);
    51                 minute = calendar.get(Calendar.MINUTE);
    52                 second = calendar.get(Calendar.SECOND);
    53                
    54                 result.append(year);
    55                 result.append('-');
    56                 if (month < 10) {
    57                         result.append('0');
    58                 }
    59                 result.append(month);
    60                 result.append('-');
    61                 if (day < 10) {
    62                         result.append('0');
    63                 }
    64                 result.append(day);
    65                 result.append('T');
    66                 if (hour < 10) {
    67                         result.append('0');
    68                 }
    69                 result.append(hour);
    70                 result.append(':');
    71                 if (minute < 10) {
    72                         result.append('0');
    73                 }
    74                 result.append(minute);
    75                 result.append(':');
    76                 if (second < 10) {
    77                         result.append('0');
    78                 }
    79                 result.append(second);
    80                 result.append('Z');
    81                
    82                 return result.toString();
    83         }
     15
     16    private GregorianCalendar calendar;
     17
     18
     19    /**
     20    * Creates a new instance.
     21    */
     22    public DateFormatter() {
     23        calendar = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
     24    }
     25
     26
     27    /**
     28    * Formats a date in XML format.
     29     *
     30    * @param date
     31    *            The date to be formatted.
     32    * @return The string representing the date.
     33    */
     34    public String format(Date date) {
     35        StringBuilder result;
     36        int year;
     37        int month;
     38        int day;
     39        int hour;
     40        int minute;
     41        int second;
     42
     43        calendar.setTime(date);
     44
     45        result = new StringBuilder(20);
     46
     47        year = calendar.get(Calendar.YEAR);
     48        month = calendar.get(Calendar.MONTH) + 1;
     49        day = calendar.get(Calendar.DATE);
     50        hour = calendar.get(Calendar.HOUR_OF_DAY);
     51        minute = calendar.get(Calendar.MINUTE);
     52        second = calendar.get(Calendar.SECOND);
     53
     54        result.append(year);
     55        result.append('-');
     56        if (month < 10) {
     57            result.append('0');
     58        }
     59        result.append(month);
     60        result.append('-');
     61        if (day < 10) {
     62            result.append('0');
     63        }
     64        result.append(day);
     65        result.append('T');
     66        if (hour < 10) {
     67            result.append('0');
     68        }
     69        result.append(hour);
     70        result.append(':');
     71        if (minute < 10) {
     72            result.append('0');
     73        }
     74        result.append(minute);
     75        result.append(':');
     76        if (second < 10) {
     77            result.append('0');
     78        }
     79        result.append(second);
     80        result.append('Z');
     81
     82        return result.toString();
     83    }
    8484}
  • trunk/src/org/openstreetmap/josm/data/osm/Node.java

    r1108 r1169  
    1919 */
    2020public final class Node extends OsmPrimitive {
    21        
    22         public LatLon coor;
    23         public volatile EastNorth eastNorth;
    24    
     21
     22    public LatLon coor;
     23    public volatile EastNorth eastNorth;
     24
    2525    private static CoordinateFormat mCord;
    26    
     26
    2727    static {
    2828        try {
     
    3333    }
    3434
    35         /**
    36          * Create an incomplete Node object
    37          */
    38         public Node(long id) {
    39                 this.id = id;
    40                 incomplete = true;
    41         }
    42        
    43         /**
    44          * Create an identical clone of the argument (including the id)
    45          */
    46         public Node(Node clone) {
    47                 cloneFrom(clone);
    48         }
     35    /**
     36     * Create an incomplete Node object
     37     */
     38    public Node(long id) {
     39        this.id = id;
     40        incomplete = true;
     41    }
    4942
    50         public Node(LatLon latlon) {
    51                 this.coor = latlon;
    52                 eastNorth = Main.proj.latlon2eastNorth(latlon);
    53         }
     43    /**
     44     * Create an identical clone of the argument (including the id)
     45     */
     46    public Node(Node clone) {
     47        cloneFrom(clone);
     48    }
    5449
    55         @Override public void visit(Visitor visitor) {
    56                 visitor.visit(this);
    57         }
    58        
    59         @Override public void cloneFrom(OsmPrimitive osm) {
    60                 super.cloneFrom(osm);
    61                 coor = ((Node)osm).coor;
    62                 eastNorth = ((Node)osm).eastNorth;
    63         }
     50    public Node(LatLon latlon) {
     51        this.coor = latlon;
     52        eastNorth = Main.proj.latlon2eastNorth(latlon);
     53    }
    6454
    65         @Override public String toString() {
    66                 if (coor == null) return "{Node id="+id+"}";
    67                 return "{Node id="+id+",version="+version+",lat="+coor.lat()+",lon="+coor.lon()+"}";
    68         }
     55    @Override public void visit(Visitor visitor) {
     56        visitor.visit(this);
     57    }
    6958
    70         @Override public boolean realEqual(OsmPrimitive osm, boolean semanticOnly) {
    71                 if (osm instanceof Node) {
     59    @Override public void cloneFrom(OsmPrimitive osm) {
     60        super.cloneFrom(osm);
     61        coor = ((Node)osm).coor;
     62        eastNorth = ((Node)osm).eastNorth;
     63    }
     64
     65    @Override public String toString() {
     66        if (coor == null) return "{Node id="+id+"}";
     67        return "{Node id="+id+",version="+version+",lat="+coor.lat()+",lon="+coor.lon()+"}";
     68    }
     69
     70    @Override public boolean realEqual(OsmPrimitive osm, boolean semanticOnly) {
     71        if (osm instanceof Node) {
    7272            if (super.realEqual(osm, semanticOnly)) {
    7373                if ((coor == null) && ((Node)osm).coor == null)
     
    8080    }
    8181
    82         public int compareTo(OsmPrimitive o) {
    83             return o instanceof Node ? Long.valueOf(id).compareTo(o.id) : 1;
    84         }
     82    public int compareTo(OsmPrimitive o) {
     83        return o instanceof Node ? Long.valueOf(id).compareTo(o.id) : 1;
     84    }
    8585
    86         public String getName() {
    87                 String name;
    88                 if (incomplete) {
    89                         name = tr("incomplete");
    90                 } else {
    91                         name = get("name");
    92                         if (name == null)
    93                                 name = id == 0 ? "" : ""+id;
    94                         name += " (" + coor.latToString(mCord) + ", " + coor.lonToString(mCord) + ")";
    95                 }
    96                 return name;
    97         }
     86    public String getName() {
     87        String name;
     88        if (incomplete) {
     89            name = tr("incomplete");
     90        } else {
     91            name = get("name");
     92            if (name == null)
     93                name = id == 0 ? "" : ""+id;
     94            name += " (" + coor.latToString(mCord) + ", " + coor.lonToString(mCord) + ")";
     95        }
     96        return name;
     97    }
    9898}
  • trunk/src/org/openstreetmap/josm/data/osm/OsmPrimitive.java

    r795 r1169  
    3030abstract public class OsmPrimitive implements Comparable<OsmPrimitive> {
    3131
    32         /**
    33         * The key/value list for this primitive.
    34         */
    35         public Map<String, String> keys;
    36 
    37         /**
    38         * Unique identifier in OSM. This is used to identify objects on the server.
    39         * An id of 0 means an unknown id. The object has not been uploaded yet to
    40         * know what id it will get.
    41         *
    42         * Do not write to this attribute except you know exactly what you are doing.
    43         * More specific, it is not good to set this to 0 and think the object is now
    44         * new to the server! To create a new object, call the default constructor of
    45         * the respective class.
    46         */
    47         public long id = 0;
    48 
    49         /**
    50         * <code>true</code> if the object has been modified since it was loaded from
    51         * the server. In this case, on next upload, this object will be updated.
    52         * Deleted objects are deleted from the server. If the objects are added (id=0),
    53         * the modified is ignored and the object is added to the server.
    54         */
    55         public boolean modified = false;
    56 
    57         /**
    58         * <code>true</code>, if the object has been deleted.
    59         */
    60         public boolean deleted = false;
    61 
    62         /**
    63         * Visibility status as specified by the server. The visible attribute was
    64         * introduced with the 0.4 API to be able to communicate deleted objects
    65         * (they will have visible=false). Currently JOSM does never deal with
    66         * these, so this is really for future use only.
    67         */
    68         public boolean visible = true;
    69 
    70         /**
    71         * User that last modified this primitive, as specified by the server.
    72         * Never changed by JOSM.
    73         */
    74         public User user = null;
    75 
    76         /**
    77         * true if this object is considered "tagged". To be "tagged", an object
    78         * must have one or more "non-standard" tags. "created_by" and "source"
    79         * are typically considered "standard" tags and do not make an object
    80         * "tagged".
    81         */
    82         public boolean tagged = false;
    83 
    84         /**
    85         * true if this object has direction dependent tags (e.g. oneway)
    86         */
    87         public boolean hasDirectionKeys = false;
    88 
    89         /**
    90         * If set to true, this object is currently selected.
    91         */
    92         public volatile boolean selected = false;
    93 
    94         /**
    95         * Time of last modification to this object. This is not set by JOSM but
    96         * read from the server and delivered back to the server unmodified. It is
    97         * used to check against edit conflicts.
    98         */
    99         public String timestamp = null;
    100 
    101         /**
    102         * The timestamp is only parsed when this is really necessary, and this
    103         * is the cache for the result.
    104         */
    105         public Date parsedTimestamp = null;
    106 
    107         /**
    108         * If set to true, this object is incomplete, which means only the id
    109         * and type is known (type is the objects instance class)
    110         */
    111         public boolean incomplete = false;
    112 
    113         /**
    114         * Contains the version number as returned by the API. Needed to
    115         * ensure update consistency
    116         */
    117         public int version = -1;
    118 
    119         /**
    120         * Contains a list of "uninteresting" keys that do not make an object
    121         * "tagged".
    122         * Initialized by checkTagged()
    123         */
    124         private static Collection<String> uninteresting = null;
    125 
    126         /**
    127         * Contains a list of direction-dependent keys that make an object
    128         * direction dependent.
    129         * Initialized by checkDirectionTagged()
    130         */
    131         private static Collection<String> directionKeys = null;
    132 
    133         /**
    134         * Implementation of the visitor scheme. Subclasses have to call the correct
    135         * visitor function.
    136         * @param visitor The visitor from which the visit() function must be called.
    137         */
    138         abstract public void visit(Visitor visitor);
    139 
    140         public final void delete(boolean deleted) {
    141                 this.deleted = deleted;
    142                 selected = false;
    143                 modified = true;
    144         }
    145 
    146         /**
    147         * Returns the timestamp for this object, or the current time if none is set.
    148         * Internally, parses the timestamp from XML into a Date object and caches it
    149         * for possible repeated calls.
    150         */
    151         public Date getTimestamp() {
    152                 if (parsedTimestamp == null) {
    153                         try {
    154                                 parsedTimestamp = DateParser.parse(timestamp);
    155                         } catch (ParseException ex) {
    156                                 parsedTimestamp = new Date();
    157                         }
    158                 }
    159                 return parsedTimestamp;
    160         }
    161 
    162         /**
    163         * Equal, if the id (and class) is equal.
    164         *
    165         * An primitive is equal to its incomplete counter part.
    166         */
    167         @Override public boolean equals(Object obj) {
    168                 if (id == 0) return obj == this;
    169                 if (obj instanceof OsmPrimitive) { // not null too
    170                         return ((OsmPrimitive)obj).id == id && obj.getClass() == getClass();
    171                 }
    172                 return false;
    173         }
    174 
    175         /**
    176         * Return the id plus the class type encoded as hashcode or super's hashcode if id is 0.
    177         *
    178         * An primitive has the same hashcode as its incomplete counterpart.
    179         */
    180         @Override public final int hashCode() {
    181                 if (id == 0)
    182                         return super.hashCode();
    183                 final int[] ret = new int[1];
    184                 Visitor v = new Visitor(){
    185                         public void visit(Node n) { ret[0] = 1; }
    186                         public void visit(Way w) { ret[0] = 2; }
    187                         public void visit(Relation e) { ret[0] = 3; }
    188                 };
    189                 visit(v);
    190                 return id == 0 ? super.hashCode() : (int)(id<<2)+ret[0];
    191         }
    192 
    193         /**
    194         * Set the given value to the given key
    195         * @param key The key, for which the value is to be set.
    196         * @param value The value for the key.
    197         */
    198         public final void put(String key, String value) {
    199                 if (value == null)
    200                         remove(key);
    201                 else {
    202                         if (keys == null)
    203                                 keys = new HashMap<String, String>();
    204                         keys.put(key, value);
    205                 }
    206                 checkTagged();
    207                 checkDirectionTagged();
    208         }
    209         /**
    210         * Remove the given key from the list.
    211         */
    212         public final void remove(String key) {
    213                 if (keys != null) {
    214                         keys.remove(key);
    215                         if (keys.isEmpty())
    216                                 keys = null;
    217                 }
    218                 checkTagged();
    219                 checkDirectionTagged();
    220         }
    221 
    222         public String getName() {
    223                 return null;
    224         }
    225 
    226         public final String get(String key) {
    227                 return keys == null ? null : keys.get(key);
    228         }
    229 
    230         public final Collection<Entry<String, String>> entrySet() {
    231                 if (keys == null)
    232                         return Collections.emptyList();
    233                 return keys.entrySet();
    234         }
    235 
    236         public final Collection<String> keySet() {
    237                 if (keys == null)
    238                         return Collections.emptyList();
    239                 return keys.keySet();
    240         }
    241 
    242         /**
    243         * Get and write all attributes from the parameter. Does not fire any listener, so
    244         * use this only in the data initializing phase
    245         */
    246         public void cloneFrom(OsmPrimitive osm) {
    247                 keys = osm.keys == null ? null : new HashMap<String, String>(osm.keys);
    248                 id = osm.id;
    249                 modified = osm.modified;
    250                 deleted = osm.deleted;
    251                 selected = osm.selected;
    252                 timestamp = osm.timestamp;
    253                 version = osm.version;
    254                 tagged = osm.tagged;
    255                 incomplete = osm.incomplete;
    256         }
    257 
    258         /**
    259         * Perform an equality compare for all non-volatile fields not only for the id
    260         * but for the whole object (for conflict resolving)
    261         * @param semanticOnly if <code>true</code>, modified flag and timestamp are not compared
    262         */
    263         public boolean realEqual(OsmPrimitive osm, boolean semanticOnly) {
    264                 return
    265                         id == osm.id &&
    266                         incomplete == osm.incomplete &&
    267                         (semanticOnly || (modified == osm.modified)) &&
    268                         deleted == osm.deleted &&
    269                         (semanticOnly || (timestamp == null ? osm.timestamp==null : timestamp.equals(osm.timestamp))) &&
    270                         (semanticOnly || (version==osm.version)) &&
    271                         (semanticOnly || (user == null ? osm.user==null : user==osm.user)) &&
    272                         (semanticOnly || (visible == osm.visible)) &&
    273                         (keys == null ? osm.keys==null : keys.equals(osm.keys));
    274         }
    275 
    276         public String getTimeStr() {
    277                 return timestamp == null ? null : new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(timestamp);
    278         }
    279 
    280         /**
    281         * Updates the "tagged" flag. "keys" property should probably be made private
    282         * to make sure this gets called when keys are set.
    283         */
    284         public void checkTagged() {
    285                 tagged = false;
    286                 if(uninteresting == null)
    287                         uninteresting = new HashSet<String>(Arrays.asList(Main.pref.get("tags.uninteresting",
    288                         "source;note;converted_by;created_by").split(";")));
    289                 if (keys != null) {
    290                         for (Entry<String,String> e : keys.entrySet()) {
    291                                 if (!uninteresting.contains(e.getKey())) {
    292                                         tagged = true;
    293                                         break;
    294                                 }
    295                         }
    296                 }
    297         }
    298         /**
    299         * Updates the "hasDirectionKeys" flag. "keys" property should probably be made private
    300         * to make sure this gets called when keys are set.
    301         */
    302         public void checkDirectionTagged() {
    303                 hasDirectionKeys = false;
    304                 if(directionKeys == null)
    305                         directionKeys = new HashSet<String>(Arrays.asList(Main.pref.get("tags.direction",
    306                         "oneway;incline;incline_step;aerialway").split(";")));
    307                 if (keys != null) {
    308                         for (Entry<String,String> e : keys.entrySet()) {
    309                                 if (directionKeys.contains(e.getKey())) {
    310                                         hasDirectionKeys = true;
    311                                         break;
    312                                 }
    313                         }
    314                 }
    315         }
     32    /**
     33    * The key/value list for this primitive.
     34    */
     35    public Map<String, String> keys;
     36
     37    /**
     38    * Unique identifier in OSM. This is used to identify objects on the server.
     39    * An id of 0 means an unknown id. The object has not been uploaded yet to
     40    * know what id it will get.
     41    *
     42    * Do not write to this attribute except you know exactly what you are doing.
     43    * More specific, it is not good to set this to 0 and think the object is now
     44    * new to the server! To create a new object, call the default constructor of
     45    * the respective class.
     46    */
     47    public long id = 0;
     48
     49    /**
     50    * <code>true</code> if the object has been modified since it was loaded from
     51    * the server. In this case, on next upload, this object will be updated.
     52    * Deleted objects are deleted from the server. If the objects are added (id=0),
     53    * the modified is ignored and the object is added to the server.
     54    */
     55    public boolean modified = false;
     56
     57    /**
     58    * <code>true</code>, if the object has been deleted.
     59    */
     60    public boolean deleted = false;
     61
     62    /**
     63    * Visibility status as specified by the server. The visible attribute was
     64    * introduced with the 0.4 API to be able to communicate deleted objects
     65    * (they will have visible=false). Currently JOSM does never deal with
     66    * these, so this is really for future use only.
     67    */
     68    public boolean visible = true;
     69
     70    /**
     71    * User that last modified this primitive, as specified by the server.
     72    * Never changed by JOSM.
     73    */
     74    public User user = null;
     75
     76    /**
     77    * true if this object is considered "tagged". To be "tagged", an object
     78    * must have one or more "non-standard" tags. "created_by" and "source"
     79    * are typically considered "standard" tags and do not make an object
     80    * "tagged".
     81    */
     82    public boolean tagged = false;
     83
     84    /**
     85    * true if this object has direction dependent tags (e.g. oneway)
     86    */
     87    public boolean hasDirectionKeys = false;
     88
     89    /**
     90    * If set to true, this object is currently selected.
     91    */
     92    public volatile boolean selected = false;
     93
     94    /**
     95    * Time of last modification to this object. This is not set by JOSM but
     96    * read from the server and delivered back to the server unmodified. It is
     97    * used to check against edit conflicts.
     98    */
     99    public String timestamp = null;
     100
     101    /**
     102    * The timestamp is only parsed when this is really necessary, and this
     103    * is the cache for the result.
     104    */
     105    public Date parsedTimestamp = null;
     106
     107    /**
     108    * If set to true, this object is incomplete, which means only the id
     109    * and type is known (type is the objects instance class)
     110    */
     111    public boolean incomplete = false;
     112
     113    /**
     114    * Contains the version number as returned by the API. Needed to
     115    * ensure update consistency
     116    */
     117    public int version = -1;
     118
     119    /**
     120    * Contains a list of "uninteresting" keys that do not make an object
     121    * "tagged".
     122    * Initialized by checkTagged()
     123    */
     124    private static Collection<String> uninteresting = null;
     125
     126    /**
     127    * Contains a list of direction-dependent keys that make an object
     128    * direction dependent.
     129    * Initialized by checkDirectionTagged()
     130    */
     131    private static Collection<String> directionKeys = null;
     132
     133    /**
     134    * Implementation of the visitor scheme. Subclasses have to call the correct
     135    * visitor function.
     136    * @param visitor The visitor from which the visit() function must be called.
     137    */
     138    abstract public void visit(Visitor visitor);
     139
     140    public final void delete(boolean deleted) {
     141        this.deleted = deleted;
     142        selected = false;
     143        modified = true;
     144    }
     145
     146    /**
     147    * Returns the timestamp for this object, or the current time if none is set.
     148    * Internally, parses the timestamp from XML into a Date object and caches it
     149    * for possible repeated calls.
     150    */
     151    public Date getTimestamp() {
     152        if (parsedTimestamp == null) {
     153            try {
     154                parsedTimestamp = DateParser.parse(timestamp);
     155            } catch (ParseException ex) {
     156                parsedTimestamp = new Date();
     157            }
     158        }
     159        return parsedTimestamp;
     160    }
     161
     162    /**
     163    * Equal, if the id (and class) is equal.
     164    *
     165    * An primitive is equal to its incomplete counter part.
     166    */
     167    @Override public boolean equals(Object obj) {
     168        if (id == 0) return obj == this;
     169        if (obj instanceof OsmPrimitive) { // not null too
     170            return ((OsmPrimitive)obj).id == id && obj.getClass() == getClass();
     171        }
     172        return false;
     173    }
     174
     175    /**
     176    * Return the id plus the class type encoded as hashcode or super's hashcode if id is 0.
     177    *
     178    * An primitive has the same hashcode as its incomplete counterpart.
     179    */
     180    @Override public final int hashCode() {
     181        if (id == 0)
     182            return super.hashCode();
     183        final int[] ret = new int[1];
     184        Visitor v = new Visitor(){
     185            public void visit(Node n) { ret[0] = 1; }
     186            public void visit(Way w) { ret[0] = 2; }
     187            public void visit(Relation e) { ret[0] = 3; }
     188        };
     189        visit(v);
     190        return id == 0 ? super.hashCode() : (int)(id<<2)+ret[0];
     191    }
     192
     193    /**
     194    * Set the given value to the given key
     195    * @param key The key, for which the value is to be set.
     196    * @param value The value for the key.
     197    */
     198    public final void put(String key, String value) {
     199        if (value == null)
     200            remove(key);
     201        else {
     202            if (keys == null)
     203                keys = new HashMap<String, String>();
     204            keys.put(key, value);
     205        }
     206        checkTagged();
     207        checkDirectionTagged();
     208    }
     209    /**
     210    * Remove the given key from the list.
     211    */
     212    public final void remove(String key) {
     213        if (keys != null) {
     214            keys.remove(key);
     215            if (keys.isEmpty())
     216                keys = null;
     217        }
     218        checkTagged();
     219        checkDirectionTagged();
     220    }
     221
     222    public String getName() {
     223        return null;
     224    }
     225
     226    public final String get(String key) {
     227        return keys == null ? null : keys.get(key);
     228    }
     229
     230    public final Collection<Entry<String, String>> entrySet() {
     231        if (keys == null)
     232            return Collections.emptyList();
     233        return keys.entrySet();
     234    }
     235
     236    public final Collection<String> keySet() {
     237        if (keys == null)
     238            return Collections.emptyList();
     239        return keys.keySet();
     240    }
     241
     242    /**
     243    * Get and write all attributes from the parameter. Does not fire any listener, so
     244    * use this only in the data initializing phase
     245    */
     246    public void cloneFrom(OsmPrimitive osm) {
     247        keys = osm.keys == null ? null : new HashMap<String, String>(osm.keys);
     248        id = osm.id;
     249        modified = osm.modified;
     250        deleted = osm.deleted;
     251        selected = osm.selected;
     252        timestamp = osm.timestamp;
     253        version = osm.version;
     254        tagged = osm.tagged;
     255        incomplete = osm.incomplete;
     256    }
     257
     258    /**
     259    * Perform an equality compare for all non-volatile fields not only for the id
     260    * but for the whole object (for conflict resolving)
     261    * @param semanticOnly if <code>true</code>, modified flag and timestamp are not compared
     262    */
     263    public boolean realEqual(OsmPrimitive osm, boolean semanticOnly) {
     264        return
     265            id == osm.id &&
     266            incomplete == osm.incomplete &&
     267            (semanticOnly || (modified == osm.modified)) &&
     268            deleted == osm.deleted &&
     269            (semanticOnly || (timestamp == null ? osm.timestamp==null : timestamp.equals(osm.timestamp))) &&
     270            (semanticOnly || (version==osm.version)) &&
     271            (semanticOnly || (user == null ? osm.user==null : user==osm.user)) &&
     272            (semanticOnly || (visible == osm.visible)) &&
     273            (keys == null ? osm.keys==null : keys.equals(osm.keys));
     274    }
     275
     276    public String getTimeStr() {
     277        return timestamp == null ? null : new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(timestamp);
     278    }
     279
     280    /**
     281    * Updates the "tagged" flag. "keys" property should probably be made private
     282    * to make sure this gets called when keys are set.
     283    */
     284    public void checkTagged() {
     285        tagged = false;
     286        if(uninteresting == null)
     287            uninteresting = new HashSet<String>(Arrays.asList(Main.pref.get("tags.uninteresting",
     288            "source;note;converted_by;created_by").split(";")));
     289        if (keys != null) {
     290            for (Entry<String,String> e : keys.entrySet()) {
     291                if (!uninteresting.contains(e.getKey())) {
     292                    tagged = true;
     293                    break;
     294                }
     295            }
     296        }
     297    }
     298    /**
     299    * Updates the "hasDirectionKeys" flag. "keys" property should probably be made private
     300    * to make sure this gets called when keys are set.
     301    */
     302    public void checkDirectionTagged() {
     303        hasDirectionKeys = false;
     304        if(directionKeys == null)
     305            directionKeys = new HashSet<String>(Arrays.asList(Main.pref.get("tags.direction",
     306            "oneway;incline;incline_step;aerialway").split(";")));
     307        if (keys != null) {
     308            for (Entry<String,String> e : keys.entrySet()) {
     309                if (directionKeys.contains(e.getKey())) {
     310                    hasDirectionKeys = true;
     311                    break;
     312                }
     313            }
     314        }
     315    }
    316316}
  • trunk/src/org/openstreetmap/josm/data/osm/OsmUtils.java

    r702 r1169  
    88public class OsmUtils {
    99
    10         static ArrayList<String> TRUE_VALUES = new ArrayList<String>(Arrays
    11                 .asList(new String[] { "true", "yes", "1", "on" }));
    12         static ArrayList<String> FALSE_VALUES = new ArrayList<String>(Arrays
    13                 .asList(new String[] { "false", "no", "0", "off" }));
     10    static ArrayList<String> TRUE_VALUES = new ArrayList<String>(Arrays
     11            .asList(new String[] { "true", "yes", "1", "on" }));
     12    static ArrayList<String> FALSE_VALUES = new ArrayList<String>(Arrays
     13            .asList(new String[] { "false", "no", "0", "off" }));
    1414
    15         public static final String trueval = "yes";
    16         public static final String falseval = "no";
     15    public static final String trueval = "yes";
     16    public static final String falseval = "no";
    1717
    18         public static Boolean getOsmBoolean(String value) {
    19                 if(value == null) return null;
    20                 String lowerValue = value.toLowerCase(Locale.ENGLISH);
    21                 if (TRUE_VALUES.contains(lowerValue)) return Boolean.TRUE;
    22                 if (FALSE_VALUES.contains(lowerValue)) return Boolean.FALSE;
    23                 return null;
    24         }
    25         public static String getNamedOsmBoolean(String value) {
    26                 Boolean res = getOsmBoolean(value);
    27                 return res == null ? value : (res ? trueval : falseval);
    28         }
     18    public static Boolean getOsmBoolean(String value) {
     19        if(value == null) return null;
     20        String lowerValue = value.toLowerCase(Locale.ENGLISH);
     21        if (TRUE_VALUES.contains(lowerValue)) return Boolean.TRUE;
     22        if (FALSE_VALUES.contains(lowerValue)) return Boolean.FALSE;
     23        return null;
     24    }
     25    public static String getNamedOsmBoolean(String value) {
     26        Boolean res = getOsmBoolean(value);
     27        return res == null ? value : (res ? trueval : falseval);
     28    }
    2929}
  • trunk/src/org/openstreetmap/josm/data/osm/Relation.java

    r1162 r1169  
    1111/**
    1212 * An relation, having a set of tags and any number (0...n) of members.
    13  * 
     13 *
    1414 * @author Frederik Ramm <frederik@remote.org>
    1515 */
    1616public final class Relation extends OsmPrimitive {
    17        
    18         /**
    19          * All members of this relation. Note that after changing this,
    20          * makeBackReferences and/or removeBackReferences should be called.
    21          */
    22         public final List<RelationMember> members = new ArrayList<RelationMember>();
    2317
    24         @Override public void visit(Visitor visitor) {
    25                 visitor.visit(this);
    26         }
     18    /**
     19     * All members of this relation. Note that after changing this,
     20     * makeBackReferences and/or removeBackReferences should be called.
     21     */
     22    public final List<RelationMember> members = new ArrayList<RelationMember>();
    2723
    28         /**
    29          * Create an identical clone of the argument (including the id)
    30          */
    31         public Relation(Relation clone) {
    32                 cloneFrom(clone);
    33         }
    34        
    35         /**
    36          * Create an incomplete Relation.
    37          */
    38         public Relation(long id) {
    39                 this.id = id;
    40                 incomplete = true;
    41         }
    42        
    43         /**
    44          * Create an empty Relation. Use this only if you set meaningful values
    45          * afterwards.
    46          */
    47         public Relation() {     
    48         }
    49        
    50         @Override public void cloneFrom(OsmPrimitive osm) {
    51                 super.cloneFrom(osm);
    52                 members.clear();
    53                 // we must not add the members themselves, but instead
    54                 // add clones of the members
    55                 for (RelationMember em : ((Relation)osm).members) {
    56                         members.add(new RelationMember(em));
    57                 }
    58         }
     24    @Override public void visit(Visitor visitor) {
     25        visitor.visit(this);
     26    }
    5927
    60         @Override public String toString() {
    61                 // return "{Relation id="+id+" version="+version+" members="+Arrays.toString(members.toArray())+"}";
    62                 // adding members in string increases memory usage a lot and overflows for looped relations
    63                 return "{Relation id="+id+" version="+version+"}";
    64         }
     28    /**
     29     * Create an identical clone of the argument (including the id)
     30     */
     31    public Relation(Relation clone) {
     32        cloneFrom(clone);
     33    }
    6534
    66         @Override public boolean realEqual(OsmPrimitive osm, boolean semanticOnly) {
    67                 return osm instanceof Relation ? super.realEqual(osm, semanticOnly) && members.equals(((Relation)osm).members) : false;
    68         }
     35    /**
     36     * Create an incomplete Relation.
     37     */
     38    public Relation(long id) {
     39        this.id = id;
     40        incomplete = true;
     41    }
    6942
    70         public int compareTo(OsmPrimitive o) {
    71             return o instanceof Relation ? Long.valueOf(id).compareTo(o.id) : -1;
    72         }
     43    /**
     44     * Create an empty Relation. Use this only if you set meaningful values
     45     * afterwards.
     46     */
     47    public Relation() {
     48    }
    7349
    74         public String getName() {
    75                 String name;
    76                 if (incomplete) {
    77                         name = tr("incomplete");
    78                 } else {
    79                         name = get("type");
    80                         // FIXME add names of members
    81                         if (name == null)
    82                                 name = tr("relation");
    83                        
    84                         name += " (";
    85                         String nameTag = get("name");
    86                         if (nameTag == null) nameTag = get("ref");
    87                         if (nameTag == null) nameTag = get("note");
    88                         if (nameTag != null) name += "\"" + nameTag + "\", ";
    89                         int mbno = members.size();
    90                         name += trn("{0} member", "{0} members", mbno, mbno) + ")";
    91                 }
    92                 return name;
    93         }
     50    @Override public void cloneFrom(OsmPrimitive osm) {
     51        super.cloneFrom(osm);
     52        members.clear();
     53        // we must not add the members themselves, but instead
     54        // add clones of the members
     55        for (RelationMember em : ((Relation)osm).members) {
     56            members.add(new RelationMember(em));
     57        }
     58    }
    9459
    95         public boolean isIncomplete() {
    96                 for (RelationMember m : members)
    97                         if (m.member == null)
    98                                 return true;
    99                 return false;
    100         }
     60    @Override public String toString() {
     61        // return "{Relation id="+id+" version="+version+" members="+Arrays.toString(members.toArray())+"}";
     62        // adding members in string increases memory usage a lot and overflows for looped relations
     63        return "{Relation id="+id+" version="+version+"}";
     64    }
     65
     66    @Override public boolean realEqual(OsmPrimitive osm, boolean semanticOnly) {
     67        return osm instanceof Relation ? super.realEqual(osm, semanticOnly) && members.equals(((Relation)osm).members) : false;
     68    }
     69
     70    public int compareTo(OsmPrimitive o) {
     71        return o instanceof Relation ? Long.valueOf(id).compareTo(o.id) : -1;
     72    }
     73
     74    public String getName() {
     75        String name;
     76        if (incomplete) {
     77            name = tr("incomplete");
     78        } else {
     79            name = get("type");
     80            // FIXME add names of members
     81            if (name == null)
     82                name = tr("relation");
     83
     84            name += " (";
     85            String nameTag = get("name");
     86            if (nameTag == null) nameTag = get("ref");
     87            if (nameTag == null) nameTag = get("note");
     88            if (nameTag != null) name += "\"" + nameTag + "\", ";
     89            int mbno = members.size();
     90            name += trn("{0} member", "{0} members", mbno, mbno) + ")";
     91        }
     92        return name;
     93    }
     94
     95    public boolean isIncomplete() {
     96        for (RelationMember m : members)
     97            if (m.member == null)
     98                return true;
     99        return false;
     100    }
    101101}
  • trunk/src/org/openstreetmap/josm/data/osm/RelationMember.java

    r627 r1169  
    22
    33/**
    4  * A linkage class that can be used by an relation to keep a list of 
     4 * A linkage class that can be used by an relation to keep a list of
    55 * members. Since membership may be qualified by a "role", a simple
    66 * list is not sufficient.
    7  * 
     7 *
    88 * @author Frederik Ramm <frederik@remote.org>
    99 */
    1010public class RelationMember {
    1111
    12         public String role;
    13         public OsmPrimitive member;
    14        
    15         /**
    16          * Default constructor. Does nothing.
    17          */
    18         public RelationMember() { }
     12    public String role;
     13    public OsmPrimitive member;
    1914
    20         public RelationMember(String role, OsmPrimitive member) {
    21                 this.role = role;
    22                 this.member = member;
    23         }
    24        
    25         /**
    26          * Copy constructor.
    27          * @param other relation member to be copied.
    28          */
    29         public RelationMember(RelationMember other) {
    30                 role = other.role;
    31                 member = other.member;
    32         }
    33        
    34         @Override public boolean equals(Object other) {
    35                 if (!(other instanceof RelationMember)) return false;
    36                 RelationMember otherMember = (RelationMember) other;
    37                 return otherMember.role.equals(role) && otherMember.member.equals(member);
    38         }
     15    /**
     16     * Default constructor. Does nothing.
     17     */
     18    public RelationMember() { }
    3919
    40         @Override public String toString() {
    41                 return '"' + role + "\"=" + member;
    42         }
     20    public RelationMember(String role, OsmPrimitive member) {
     21        this.role = role;
     22        this.member = member;
     23    }
     24
     25    /**
     26     * Copy constructor.
     27     * @param other relation member to be copied.
     28     */
     29    public RelationMember(RelationMember other) {
     30        role = other.role;
     31        member = other.member;
     32    }
     33
     34    @Override public boolean equals(Object other) {
     35        if (!(other instanceof RelationMember)) return false;
     36        RelationMember otherMember = (RelationMember) other;
     37        return otherMember.role.equals(role) && otherMember.member.equals(member);
     38    }
     39
     40    @Override public String toString() {
     41        return '"' + role + "\"=" + member;
     42    }
    4343}
  • trunk/src/org/openstreetmap/josm/data/osm/TigerUtils.java

    r627 r1169  
    55import java.util.TreeSet;
    66
    7 /** 
     7/**
    88 * A simple class to keep helper functions for merging TIGER data
    9  * 
     9 *
    1010 * @author daveh
    1111 *
    1212 */
    1313public class TigerUtils {
    14        
    15         public static boolean isTigerTag(String tag)
    16         {       
    17                 if (tag.indexOf("tiger:") == -1)
    18                         return false;
    19                 return true;
    20         }
    21        
    22         public static boolean tagIsInt(String name) {
    23                 if (name.equals("tiger:tlid"))
    24                         return true;
    25                 return false;
    26         }
    27        
    28         public static Object tagObj(String name) {
    29                 if (tagIsInt(name))
    30                         return new Integer(name);
    31                 return name;
    32         }
    33        
    34         public static String combineTags(String name, Set<String> values) {
     14
     15    public static boolean isTigerTag(String tag)
     16    {
     17        if (tag.indexOf("tiger:") == -1)
     18            return false;
     19        return true;
     20    }
     21
     22    public static boolean tagIsInt(String name) {
     23        if (name.equals("tiger:tlid"))
     24            return true;
     25        return false;
     26    }
     27
     28    public static Object tagObj(String name) {
     29        if (tagIsInt(name))
     30            return new Integer(name);
     31        return name;
     32    }
     33
     34    public static String combineTags(String name, Set<String> values) {
    3535        TreeSet<Object> resultSet = new TreeSet<Object>();
    3636        for (String value: values) {
    37                 for (String part: value.split(":")) {
     37            for (String part: value.split(":")) {
    3838               resultSet.add(tagObj(part));
    3939            }
     
    4242        for (Object part : resultSet) {
    4343            if (combined.length() > 0)
    44                         combined += ":";
    45                 combined += part;
     44                combined += ":";
     45            combined += part;
    4646        }
    47                 return combined;
    48         }
    49        
    50         public static String combineTags(String name, String t1, String t2) {
    51                 Set<String> set = new TreeSet<String>();
    52                 set.add(t1);
    53                 set.add(t2);
    54                 return TigerUtils.combineTags(name, set);
    55         }
     47        return combined;
     48    }
     49
     50    public static String combineTags(String name, String t1, String t2) {
     51        Set<String> set = new TreeSet<String>();
     52        set.add(t1);
     53        set.add(t2);
     54        return TigerUtils.combineTags(name, set);
     55    }
    5656}
  • trunk/src/org/openstreetmap/josm/data/osm/User.java

    r655 r1169  
    44import java.util.HashMap;
    55
    6 /** 
     6/**
    77 * A simple class to keep a list of user names.
    8  * 
     8 *
    99 * Instead of storing user names as strings with every OSM primitive, we store
    1010 * a reference to an user object, and make sure that for each username there
    1111 * is only one user object.
    12  * 
     12 *
    1313 * @author fred
    1414 *
     
    1616public class User {
    1717
    18         /** storage for existing User objects. */
    19         private static HashMap<String,User> userMap = new HashMap<String,User>();
    20        
    21         /** the username. */
    22         public String name;
    23        
    24         /** private constructor, only called from get method. */
    25         private User(String name) {
    26                 this.name = name;
    27         }
    28        
    29         /** returns a new or existing User object that represents the given name. */
    30         public static User get(String name) {
    31                 User user = userMap.get(name);
    32                 if (user == null) {
    33                         user = new User(name);
    34                         userMap.put(name, user);
    35                 }
    36                 return user;
    37         }
     18    /** storage for existing User objects. */
     19    private static HashMap<String,User> userMap = new HashMap<String,User>();
     20
     21    /** the username. */
     22    public String name;
     23
     24    /** private constructor, only called from get method. */
     25    private User(String name) {
     26        this.name = name;
     27    }
     28
     29    /** returns a new or existing User object that represents the given name. */
     30    public static User get(String name) {
     31        User user = userMap.get(name);
     32        if (user == null) {
     33            user = new User(name);
     34            userMap.put(name, user);
     35        }
     36        return user;
     37    }
    3838}
  • trunk/src/org/openstreetmap/josm/data/osm/Way.java

    r755 r1169  
    2121public final class Way extends OsmPrimitive {
    2222
    23         /**
    24         * All way nodes in this way
    25         */
    26         public final List<Node> nodes = new ArrayList<Node>();
     23    /**
     24    * All way nodes in this way
     25    */
     26    public final List<Node> nodes = new ArrayList<Node>();
    2727
    28         public void visitNodes(Visitor v) {
    29                 for (Node n : this.nodes)
    30                         v.visit(n);
    31         }
     28    public void visitNodes(Visitor v) {
     29        for (Node n : this.nodes)
     30            v.visit(n);
     31    }
    3232
    33         public ArrayList<Pair<Node,Node>> getNodePairs(boolean sort) {
    34                 ArrayList<Pair<Node,Node>> chunkSet = new ArrayList<Pair<Node,Node>>();
    35                 Node lastN = null;
    36                 for (Node n : this.nodes) {
     33    public ArrayList<Pair<Node,Node>> getNodePairs(boolean sort) {
     34        ArrayList<Pair<Node,Node>> chunkSet = new ArrayList<Pair<Node,Node>>();
     35        Node lastN = null;
     36        for (Node n : this.nodes) {
    3737            if (lastN == null) {
    38                     lastN = n;
    39                         continue;
    40                     }
    41                         Pair<Node,Node> np = new Pair<Node,Node>(lastN, n);
    42                 if (sort) {
    43                         Pair.sort(np);
    44                 }
    45                 chunkSet.add(np);
    46                 lastN = n;
    47                 }
    48                 return chunkSet;
    49         }
     38                lastN = n;
     39                continue;
     40            }
     41            Pair<Node,Node> np = new Pair<Node,Node>(lastN, n);
     42            if (sort) {
     43                Pair.sort(np);
     44            }
     45            chunkSet.add(np);
     46            lastN = n;
     47        }
     48        return chunkSet;
     49    }
    5050
    5151
    52         @Override public void visit(Visitor visitor) {
    53                 visitor.visit(this);
    54         }
     52    @Override public void visit(Visitor visitor) {
     53        visitor.visit(this);
     54    }
    5555
    56         /**
    57         * Create an identical clone of the argument (including the id)
    58         */
    59         public Way(Way clone) {
    60             cloneFrom(clone);           
    61         }
    62        
    63         /**
    64          * Create an empty way without id. Use this only if you set meaningful
    65         * values yourself.
    66         */
    67         public Way() {
    68         }
    69        
    70         /**
    71         * Create an incomplete Way.
    72         */
    73         public Way(long id) {
    74                 this.id = id;
    75                 incomplete = true;
    76         }
    77        
    78         @Override public void cloneFrom(OsmPrimitive osm) {
    79                 super.cloneFrom(osm);
    80                 nodes.clear();
    81                 nodes.addAll(((Way)osm).nodes);
     56    /**
     57    * Create an identical clone of the argument (including the id)
     58    */
     59    public Way(Way clone) {
     60            cloneFrom(clone);
     61    }
     62
     63    /**
     64     * Create an empty way without id. Use this only if you set meaningful
     65    * values yourself.
     66    */
     67    public Way() {
     68    }
     69
     70    /**
     71    * Create an incomplete Way.
     72    */
     73    public Way(long id) {
     74        this.id = id;
     75        incomplete = true;
     76    }
     77
     78    @Override public void cloneFrom(OsmPrimitive osm) {
     79        super.cloneFrom(osm);
     80        nodes.clear();
     81        nodes.addAll(((Way)osm).nodes);
    8282                checkDirectionTagged();
    83         }
     83    }
    8484
    8585    @Override public String toString() {
     
    8787    }
    8888
    89         @Override public boolean realEqual(OsmPrimitive osm, boolean semanticOnly) {
    90                 return osm instanceof Way ? super.realEqual(osm, semanticOnly) && nodes.equals(((Way)osm).nodes) : false;
     89    @Override public boolean realEqual(OsmPrimitive osm, boolean semanticOnly) {
     90        return osm instanceof Way ? super.realEqual(osm, semanticOnly) && nodes.equals(((Way)osm).nodes) : false;
    9191    }
    9292
    93         public int compareTo(OsmPrimitive o) {
    94                 if(o instanceof Relation)
    95                         return 1;
    96                 return o instanceof Way ? Long.valueOf(id).compareTo(o.id) : -1;
    97         }
     93    public int compareTo(OsmPrimitive o) {
     94        if(o instanceof Relation)
     95            return 1;
     96        return o instanceof Way ? Long.valueOf(id).compareTo(o.id) : -1;
     97    }
    9898
    99         public String getName() {
    100                 String name;
    101                 if (incomplete) {
    102                         name = tr("incomplete");
    103                 } else {
    104                         name = get("name");
    105                         if (name == null) name = get("ref");
    106                         if (name == null) {
    107                                 name =
    108                                         (get("highway") != null) ? tr("highway") :
    109                                         (get("railway") != null) ? tr("railway") :
    110                                         (get("waterway") != null) ? tr("waterway") :
    111                                         (get("landuse") != null) ? tr("landuse") : "";
    112                         }
     99    public String getName() {
     100        String name;
     101        if (incomplete) {
     102            name = tr("incomplete");
     103        } else {
     104            name = get("name");
     105            if (name == null) name = get("ref");
     106            if (name == null) {
     107                name =
     108                    (get("highway") != null) ? tr("highway") :
     109                    (get("railway") != null) ? tr("railway") :
     110                    (get("waterway") != null) ? tr("waterway") :
     111                    (get("landuse") != null) ? tr("landuse") : "";
     112            }
    113113
    114                         int nodesNo = new HashSet<Node>(nodes).size();
    115                         name += trn(" ({0} node)", " ({0} nodes)", nodesNo, nodesNo);
    116                 }
    117                 return name;
    118         }
     114            int nodesNo = new HashSet<Node>(nodes).size();
     115            name += trn(" ({0} node)", " ({0} nodes)", nodesNo, nodesNo);
     116        }
     117        return name;
     118    }
    119119
    120         @Deprecated
    121         public boolean isIncomplete() {
    122                 return incomplete;
    123         }
     120    @Deprecated
     121    public boolean isIncomplete() {
     122        return incomplete;
     123    }
    124124}
  • trunk/src/org/openstreetmap/josm/data/osm/WaySegment.java

    r627 r1169  
    66 */
    77public final class WaySegment {
    8         /**
    9         * The way.
    10         */
    11         public Way way;
     8    /**
     9    * The way.
     10    */
     11    public Way way;
    1212
    13         /**
    14         * The index of one of the 2 nodes in the way.  The other node has the
    15         * index <code>lowerIndex + 1</code>.
    16         */
    17         public int lowerIndex;
     13    /**
     14    * The index of one of the 2 nodes in the way.  The other node has the
     15    * index <code>lowerIndex + 1</code>.
     16    */
     17    public int lowerIndex;
    1818
    19         public WaySegment(Way w, int i) {
    20                 way = w;
    21                 lowerIndex = i;
    22         }
     19    public WaySegment(Way w, int i) {
     20        way = w;
     21        lowerIndex = i;
     22    }
    2323
    24         @Override public boolean equals(Object o) {
    25                 return o != null && o instanceof WaySegment
    26                         && ((WaySegment) o).way == way
    27                         && ((WaySegment) o).lowerIndex == lowerIndex;
    28         }
     24    @Override public boolean equals(Object o) {
     25        return o != null && o instanceof WaySegment
     26            && ((WaySegment) o).way == way
     27            && ((WaySegment) o).lowerIndex == lowerIndex;
     28    }
    2929
    30         @Override public int hashCode() {
    31                 return way.hashCode() ^ lowerIndex;
    32         }
     30    @Override public int hashCode() {
     31        return way.hashCode() ^ lowerIndex;
     32    }
    3333}
  • trunk/src/org/openstreetmap/josm/data/osm/visitor/AddVisitor.java

    r655 r1169  
    99/**
    1010 * Visitor that adds the visited object to the dataset given at constructor.
    11  * 
     11 *
    1212 * Is not capable of adding keys.
    13  * 
     13 *
    1414 * @author imi
    1515 */
    1616public class AddVisitor implements Visitor {
    17        
    18         protected final DataSet ds;
    19        
    20         public AddVisitor(DataSet ds) {
    21                 this.ds = ds;
    22         }
    23        
    24         public void visit(Node n) {
    25                 ds.nodes.add(n);
    26         }
    27         public void visit(Way w) {
    28                 ds.ways.add(w);
    29         }
    30         public void visit(Relation e) {
    31                 ds.relations.add(e);
    32         }
     17
     18    protected final DataSet ds;
     19
     20    public AddVisitor(DataSet ds) {
     21        this.ds = ds;
     22    }
     23
     24    public void visit(Node n) {
     25        ds.nodes.add(n);
     26    }
     27    public void visit(Way w) {
     28        ds.ways.add(w);
     29    }
     30    public void visit(Relation e) {
     31        ds.relations.add(e);
     32    }
    3333}
  • trunk/src/org/openstreetmap/josm/data/osm/visitor/AllNodesVisitor.java

    r627 r1169  
    1313/**
    1414 * Collect all nodes a specific osm primitive has.
    15  * 
     15 *
    1616 * @author imi
    1717 */
    1818public class AllNodesVisitor implements Visitor {
    1919
    20         /**
    21         * The resulting nodes collected so far.
    22         */
    23         public Collection<Node> nodes = new HashSet<Node>();
     20    /**
     21    * The resulting nodes collected so far.
     22    */
     23    public Collection<Node> nodes = new HashSet<Node>();
    2424
    25         /**
    26         * Nodes have only itself as nodes.
    27         */
    28         public void visit(Node n) {
    29                 nodes.add(n);
    30         }
     25    /**
     26    * Nodes have only itself as nodes.
     27    */
     28    public void visit(Node n) {
     29        nodes.add(n);
     30    }
    3131
    32         /**
    33         * Ways have their way nodes.
    34         */
    35         public void visit(Way w) {
    36                 w.visitNodes(this);
    37         }
     32    /**
     33    * Ways have their way nodes.
     34    */
     35    public void visit(Way w) {
     36        w.visitNodes(this);
     37    }
    3838
    39         /**
    40         * Relations may have any number of nodes.
    41         * FIXME: do we want to collect nodes from segs/ways that are relation members?
    42         * if so, use AutomatchVisitor!
    43         */
    44         public void visit(Relation e) {
    45                 for (RelationMember m : e.members)
    46                         if (m.member instanceof Node) visit((Node)m.member);
    47         }
    48         /**
    49         * @return All nodes the given primitive has.
    50         */
    51         public static Collection<Node> getAllNodes(Collection<? extends OsmPrimitive> osms) {
    52                 AllNodesVisitor v = new AllNodesVisitor();
    53                 for (OsmPrimitive osm : osms)
    54                         osm.visit(v);
    55                 return v.nodes;
    56         }
     39    /**
     40    * Relations may have any number of nodes.
     41    * FIXME: do we want to collect nodes from segs/ways that are relation members?
     42    * if so, use AutomatchVisitor!
     43    */
     44    public void visit(Relation e) {
     45        for (RelationMember m : e.members)
     46            if (m.member instanceof Node) visit((Node)m.member);
     47    }
     48    /**
     49    * @return All nodes the given primitive has.
     50    */
     51    public static Collection<Node> getAllNodes(Collection<? extends OsmPrimitive> osms) {
     52        AllNodesVisitor v = new AllNodesVisitor();
     53        for (OsmPrimitive osm : osms)
     54            osm.visit(v);
     55        return v.nodes;
     56    }
    5757}
  • trunk/src/org/openstreetmap/josm/data/osm/visitor/BoundingXYVisitor.java

    r948 r1169  
    1212
    1313/**
    14  * Calculates the total bounding rectangle of a series of {@link OsmPrimitive} objects, using the 
     14 * Calculates the total bounding rectangle of a series of {@link OsmPrimitive} objects, using the
    1515 * EastNorth values as reference.
    1616 * @author imi
     
    6161
    6262    /**
    63      * Enlarges the calculated bounding box by 0.0001 degrees. 
     63     * Enlarges the calculated bounding box by 0.0001 degrees.
    6464     * If the bounding box has not been set (<code>min</code> or <code>max</code>
    6565     * equal <code>null</code>) this method does not do anything.
    66      *   
     66     *
    6767     * @param enlargeDegree
    6868     */
     
    7272
    7373    /**
    74      * Enlarges the calculated bounding box by the specified number of degrees. 
     74     * Enlarges the calculated bounding box by the specified number of degrees.
    7575     * If the bounding box has not been set (<code>min</code> or <code>max</code>
    7676     * equal <code>null</code>) this method does not do anything.
    77      *   
     77     *
    7878     * @param enlargeDegree
    7979     */
  • trunk/src/org/openstreetmap/josm/data/osm/visitor/CollectBackReferencesVisitor.java

    r627 r1169  
    1414/**
    1515 * Helper that collect all ways a node is part of.
    16  * 
     16 *
    1717 * Deleted objects are not collected.
    18  * 
     18 *
    1919 * @author imi
    2020 */
    2121public class CollectBackReferencesVisitor implements Visitor {
    2222
    23         private final DataSet ds;
    24         private final boolean indirectRefs;
     23    private final DataSet ds;
     24    private final boolean indirectRefs;
    2525
    26         /**
    27         * The result list of primitives stored here.
    28         */
    29         public final Collection<OsmPrimitive> data = new HashSet<OsmPrimitive>();
     26    /**
     27    * The result list of primitives stored here.
     28    */
     29    public final Collection<OsmPrimitive> data = new HashSet<OsmPrimitive>();
    3030
    3131
    32         /**
    33         * Construct a back reference counter.
    34         * @param ds The dataset to operate on.
    35         */
    36         public CollectBackReferencesVisitor(DataSet ds) {
    37                 this.ds = ds;
    38                 this.indirectRefs = true;
    39         }
     32    /**
     33    * Construct a back reference counter.
     34    * @param ds The dataset to operate on.
     35    */
     36    public CollectBackReferencesVisitor(DataSet ds) {
     37        this.ds = ds;
     38        this.indirectRefs = true;
     39    }
    4040
    41         public CollectBackReferencesVisitor(DataSet ds, boolean indirectRefs) {
    42                 this.ds = ds;
    43                 this.indirectRefs = indirectRefs;
    44         }
     41    public CollectBackReferencesVisitor(DataSet ds, boolean indirectRefs) {
     42        this.ds = ds;
     43        this.indirectRefs = indirectRefs;
     44    }
    4545
    46         public void visit(Node n) {
    47                 for (Way w : ds.ways) {
    48                         if (w.deleted || w.incomplete) continue;
    49                         for (Node n2 : w.nodes) {
    50                                 if (n == n2) {
    51                                         data.add(w);
    52                                         if (indirectRefs) {
    53                                                 visit(w);
    54                                         }
    55                                 }
    56                         }
    57                 }
    58                 checkRelationMembership(n);
    59         }
    60        
    61         public void visit(Way w) {
    62                 checkRelationMembership(w);
    63         }
    64        
    65         public void visit(Relation r) {
    66                 checkRelationMembership(r);
    67         }
    68        
    69         private void checkRelationMembership(OsmPrimitive p) {
    70                 // FIXME - this might be a candidate for optimisation
    71                 // if OSM primitives are made to hold a list of back
    72                 // references.
    73                 for (Relation r : ds.relations) {
    74                         if (r.incomplete || r.deleted) continue;
    75                         for (RelationMember m : r.members) {
    76                                 if (m.member == p) {
    77                                         if (!data.contains(r)) {
    78                                                 data.add(r);
    79                                                 if (indirectRefs) {
    80                                                         // move up the tree (there might be relations
    81                                                         // referring to this relation)
    82                                                         checkRelationMembership(r);
    83                                                 }
    84                                         }
    85                                         break;
    86                                 }
    87                         }
    88                 }
    89         }
     46    public void visit(Node n) {
     47        for (Way w : ds.ways) {
     48            if (w.deleted || w.incomplete) continue;
     49            for (Node n2 : w.nodes) {
     50                if (n == n2) {
     51                    data.add(w);
     52                    if (indirectRefs) {
     53                        visit(w);
     54                    }
     55                }
     56            }
     57        }
     58        checkRelationMembership(n);
     59    }
     60
     61    public void visit(Way w) {
     62        checkRelationMembership(w);
     63    }
     64
     65    public void visit(Relation r) {
     66        checkRelationMembership(r);
     67    }
     68
     69    private void checkRelationMembership(OsmPrimitive p) {
     70        // FIXME - this might be a candidate for optimisation
     71        // if OSM primitives are made to hold a list of back
     72        // references.
     73        for (Relation r : ds.relations) {
     74            if (r.incomplete || r.deleted) continue;
     75            for (RelationMember m : r.members) {
     76                if (m.member == p) {
     77                    if (!data.contains(r)) {
     78                        data.add(r);
     79                        if (indirectRefs) {
     80                            // move up the tree (there might be relations
     81                            // referring to this relation)
     82                            checkRelationMembership(r);
     83                        }
     84                    }
     85                    break;
     86                }
     87            }
     88        }
     89    }
    9090}
  • trunk/src/org/openstreetmap/josm/data/osm/visitor/CreateOsmChangeVisitor.java

    r1071 r1169  
    1616/**
    1717 * Creates an OsmChange document from JOSM edits.
    18  * See http://wiki.openstreetmap.org/index.php/OsmChange for a documentation of the 
     18 * See http://wiki.openstreetmap.org/index.php/OsmChange for a documentation of the
    1919 * OsmChange format.
    20  * 
     20 *
    2121 * @author fred
    2222 *
     
    3030    StringWriter swriter;
    3131    OsmWriter osmwriter;
    32    
     32
    3333    public CreateOsmChangeVisitor(Changeset changeset) {
    3434        writer = new PrintWriter(swriter = new StringWriter());
    3535        writer.write("<osmChange version=\"");
    3636        writer.write(Main.pref.get("osm-server.version", "0.6"));
    37         writer.write("\" generator=\"JOSM\">\n"); 
     37        writer.write("\" generator=\"JOSM\">\n");
    3838        this.changeset = changeset;
    3939        osmwriter = new OsmWriter(writer, false, changeset);
    4040    }
    41    
     41
    4242    public void visit(Node n) {
    4343        if (n.deleted) {
     
    5555        }
    5656    }
    57    
     57
    5858    public void visit(Way w) {
    5959        if (w.deleted) {
     
    6969            switchMode((w.id == 0) ? "create" : "modify");
    7070            w.visit(osmwriter);
    71         }       
     71        }
    7272    }
    73    
     73
    7474    public void visit(Relation r) {
    7575        if (r.deleted) {
     
    8585            switchMode((r.id == 0) ? "create" : "modify");
    8686            r.visit(osmwriter);
    87         }           
     87        }
    8888    }
    89    
     89
    9090    private void switchMode(String newMode) {
    9191        if ((newMode != null && !newMode.equals(currentMode))||(newMode == null && currentMode != null)) {
     
    105105        }
    106106    }
    107    
     107
    108108    public String getDocument() {
    109109        switchMode(null);
    110110        return swriter.toString() + "</osmChange>\n";
    111111    }
    112    
     112
    113113    public Map<OsmPrimitive,Long> getNewIdMap() {
    114114        return osmwriter.usedNewIds;
  • trunk/src/org/openstreetmap/josm/data/osm/visitor/DeleteVisitor.java

    r655 r1169  
    99/**
    1010 * Visitor that adds the visited object to the dataset given at constructor.
    11  * 
     11 *
    1212 * Is not capable of adding keys.
    13  * 
     13 *
    1414 * @author imi
    1515 */
    1616public class DeleteVisitor implements Visitor {
    17        
    18         private final DataSet ds;
    19        
    20         public DeleteVisitor(DataSet ds) {
    21                 this.ds = ds;
    22         }
    23        
    24         public void visit(Node n) {
    25                 ds.nodes.remove(n);
    26         }
    27         public void visit(Way w) {
    28                 ds.ways.remove(w);
    29         }
    30         public void visit(Relation e) {
    31                 ds.relations.remove(e);
    32         }
     17
     18    private final DataSet ds;
     19
     20    public DeleteVisitor(DataSet ds) {
     21        this.ds = ds;
     22    }
     23
     24    public void visit(Node n) {
     25        ds.nodes.remove(n);
     26    }
     27    public void visit(Way w) {
     28        ds.ways.remove(w);
     29    }
     30    public void visit(Relation e) {
     31        ds.relations.remove(e);
     32    }
    3333}
  • trunk/src/org/openstreetmap/josm/data/osm/visitor/MapPaintVisitor.java

    r1077 r1169  
    3333
    3434public class MapPaintVisitor extends SimplePaintVisitor {
    35         protected boolean useRealWidth;
    36         protected boolean zoomLevelDisplay;
    37         protected boolean fillAreas;
    38         protected int fillAlpha;
    39         protected Color untaggedColor;
    40         protected Color textColor;
    41         protected boolean currentDashed = false;
    42         protected int currentWidth = 0;
    43         protected Stroke currentStroke = null;
    44         protected Font orderFont;
    45         protected ElemStyles styles;
    46         protected double circum;
    47         protected String regionalNameOrder[];
    48 
    49         protected boolean isZoomOk(ElemStyle e) {
    50                 if (!zoomLevelDisplay) /* show everything if the user wishes so */
    51                         return true;
    52 
    53                 if(e == null) /* the default for things that don't have a rule (show, if scale is smaller than 1500m) */
    54                         return (circum < 1500);
    55 
    56                 // formula to calculate a map scale: natural size / map size = scale
    57                 // example: 876000mm (876m as displayed) / 22mm (roughly estimated screen size of legend bar) = 39818
    58                 //
    59                 // so the exact "correcting value" below depends only on the screen size and resolution
    60                 // XXX - do we need a Preference setting for this (if things vary widely)?
    61                 return !(circum >= e.maxScale / 22 || circum < e.minScale / 22);
    62         }
    63 
    64         /**
    65         * Draw a small rectangle.
    66         * White if selected (as always) or red otherwise.
    67         *
    68         * @param n The node to draw.
    69         */
    70         public void visit(Node n) {
    71                 IconElemStyle nodeStyle = styles.get(n);
    72                 if (nodeStyle != null && isZoomOk(nodeStyle))
    73                         drawNode(n, nodeStyle.icon, nodeStyle.annotate);
    74                 else if (n.selected)
    75                         drawNode(n, selectedColor, selectedNodeSize, selectedNodeRadius, fillSelectedNode);
    76                 else if (n.tagged)
    77                         drawNode(n, nodeColor, taggedNodeSize, taggedNodeRadius, fillUnselectedNode);
    78                 else
    79                         drawNode(n, nodeColor, unselectedNodeSize, unselectedNodeRadius, fillUnselectedNode);
    80         }
    81 
    82         /**
    83         * Draw a line for all segments, according to tags.
    84         * @param w The way to draw.
    85         */
    86         public void visit(Way w) {
    87                 if(w.nodes.size() < 2)
    88                         return;
    89                 // show direction arrows, if draw.segment.relevant_directions_only is not set, the way is tagged with a direction key
    90                 // (even if the tag is negated as in oneway=false) or the way is selected
    91                 boolean showDirection = w.selected || ((!useRealWidth) && (showDirectionArrow
    92                 && (!showRelevantDirectionsOnly || w.hasDirectionKeys)));
    93 
    94                 Color color = untaggedColor;
    95                 int width = defaultSegmentWidth;
    96                 int realWidth = 0; //the real width of the element in meters
    97                 boolean dashed = false;
    98                 ElemStyle wayStyle = styles.get(w);
    99 
    100                 if(!isZoomOk(wayStyle))
    101                         return;
    102 
    103                 LineElemStyle l = null;
    104                 if(wayStyle!=null)
    105                 {
    106                         Color areacolor = untaggedColor;
    107                         boolean area = false;
    108                         if(wayStyle instanceof LineElemStyle)
    109                                 l = (LineElemStyle)wayStyle;
    110                         else if (wayStyle instanceof AreaElemStyle)
    111                         {
    112                                 areacolor = ((AreaElemStyle)wayStyle).color;
    113                                 color = areacolor;
    114                                 l = ((AreaElemStyle)wayStyle).line;
    115                                 area = true;
    116                         }
    117                         if(l != null)
    118                         {
    119                                 color = l.color;
    120                                 width = l.width;
    121                                 realWidth = l.realWidth;
    122                                 dashed = l.dashed;
    123                         }
    124                         if (area && fillAreas)
    125                                 drawWayAsArea(w, areacolor);
    126                 }
    127 
    128                 if (realWidth > 0 && useRealWidth && !showDirection)
    129                 {
    130                         int tmpWidth = (int) (100 /  (float) (circum / realWidth));
    131                         if (tmpWidth > width) width = tmpWidth;
    132                 }
    133                 if(w.selected)
    134                         color = selectedColor;
    135 
    136                 Node lastN;
    137                 if(l != null && l.overlays != null)
    138                 {
    139                         for(LineElemStyle s : l.overlays)
    140                         {
    141                                 if(!s.over)
    142                                 {
    143                                         lastN = null;
    144                                         for(Node n : w.nodes)
    145                                         {
    146                                                 if(lastN != null)
    147                                                 {
    148                                                         drawSeg(lastN, n, s.color != null  && !w.selected ? s.color : color,
    149                                                         false, s.getWidth(width), s.dashed);
    150                                                 }
    151                                                 lastN = n;
    152                                         }
    153                                 }
    154                         }
    155                 }
    156 
    157                 lastN = null;
    158                 for(Node n : w.nodes)
    159                 {
    160                         if(lastN != null)
    161                                 drawSeg(lastN, n, color, showDirection, width, dashed);
    162                         lastN = n;
    163                 }
    164 
    165                 if(l != null && l.overlays != null)
    166                 {
    167                         for(LineElemStyle s : l.overlays)
    168                         {
    169                                 if(s.over)
    170                                 {
    171                                         lastN = null;
    172                                         for(Node n : w.nodes)
    173                                         {
    174                                                 if(lastN != null)
    175                                                 {
    176                                                         drawSeg(lastN, n, s.color != null && !w.selected ? s.color : color,
    177                                                         false, s.getWidth(width), s.dashed);
    178                                                 }
    179                                                 lastN = n;
    180                                         }
    181                                 }
    182                         }
    183                 }
    184 
    185                 if(showOrderNumber)
    186                 {
    187                         int orderNumber = 0;
    188                         lastN = null;
    189                         for(Node n : w.nodes)
    190                         {
    191                                 if(lastN != null)
    192                                 {
    193                                         orderNumber++;
    194                                         drawOrderNumber(lastN, n, orderNumber);
    195                                 }
    196                                 lastN = n;
    197                         }
    198                 }
    199                 displaySegments();
    200         }
    201 
    202         public void visit(Relation e) {
    203                 // relations are not (yet?) drawn.
    204         }
    205 
    206         // This assumes that all segments are aligned in the same direction!
    207         protected void drawWayAsArea(Way w, Color color)
    208         {
    209                 Polygon polygon = new Polygon();
    210 
    211                 for (Node n : w.nodes)
    212                 {
    213                         Point p = nc.getPoint(n.eastNorth);
    214                         polygon.addPoint(p.x,p.y);
    215                 }
    216 
    217                 Color mycolor = w.selected ? selectedColor : color;
    218                 // set the opacity (alpha) level of the filled polygon
    219                 g.setColor(new Color( mycolor.getRed(), mycolor.getGreen(), mycolor.getBlue(), fillAlpha));
    220 
    221                 g.fillPolygon(polygon);
    222         }
    223 
    224         // NEW
    225         protected void drawNode(Node n, ImageIcon icon, boolean annotate) {
    226                 Point p = nc.getPoint(n.eastNorth);
    227                 if ((p.x < 0) || (p.y < 0) || (p.x > nc.getWidth()) || (p.y > nc.getHeight())) return;
    228                 int w = icon.getIconWidth(), h=icon.getIconHeight();
    229                 icon.paintIcon ( Main.map.mapView, g, p.x-w/2, p.y-h/2 );
    230                 String name = getNodeName(n);
    231                 if (name!=null && annotate)
    232                 {
    233                         g.setColor(textColor);
    234                         Font defaultFont = g.getFont();
    235                         g.setFont (orderFont);
    236                         g.drawString (name, p.x+w/2+2, p.y+h/2+2);
    237                         g.setFont(defaultFont);
    238                 }
    239                 if (n.selected)
    240                 {
    241                         g.setColor (  selectedColor );
    242                         g.drawRect (p.x-w/2-2,p.y-w/2-2, w+4, h+4);
    243                 }
    244         }
    245 
    246         protected String getNodeName(Node n) {
    247                 String name = null;
    248                 if (n.keys != null) {
    249                         for (int i = 0; i < regionalNameOrder.length; i++) {
    250                                 name = n.keys.get(regionalNameOrder[i]);
    251                                 if (name != null) break;
    252                         }
    253                 }
    254                 return name;
    255         }
    256 
    257         private void drawSeg(Node n1, Node n2, Color col, boolean showDirection, int width, boolean dashed) {
    258                 if (col != currentColor || width != currentWidth || dashed != currentDashed) {
    259                         displaySegments(col, width, dashed);
    260                 }
    261                 Point p1 = nc.getPoint(n1.eastNorth);
    262                 Point p2 = nc.getPoint(n2.eastNorth);
    263 
    264                 if (!isSegmentVisible(p1, p2)) {
    265                         return;
    266                 }
    267                 currentPath.moveTo(p1.x, p1.y);
    268                 currentPath.lineTo(p2.x, p2.y);
    269 
    270                 if (showDirection) {
    271                         double t = Math.atan2(p2.y-p1.y, p2.x-p1.x) + Math.PI;
    272                         currentPath.lineTo((int)(p2.x + 10*Math.cos(t-PHI)), (int)(p2.y + 10*Math.sin(t-PHI)));
    273                         currentPath.moveTo((int)(p2.x + 10*Math.cos(t+PHI)), (int)(p2.y + 10*Math.sin(t+PHI)));
    274                         currentPath.lineTo(p2.x, p2.y);
    275                 }
    276         }
    277 
    278         protected void displaySegments() {
    279                 displaySegments(null, 0, false);
    280         }
    281 
    282         protected void displaySegments(Color newColor, int newWidth, boolean newDash) {
    283                 if (currentPath != null) {
    284                         Graphics2D g2d = (Graphics2D)g;
    285                         g2d.setColor(inactive ? inactiveColor : currentColor);
    286                         if (currentStroke == null) {
    287                                 if (currentDashed)
    288                                         g2d.setStroke(new BasicStroke(currentWidth,BasicStroke.CAP_BUTT,BasicStroke.JOIN_ROUND,0,new float[] {9},0));
    289                                 else
    290                                         g2d.setStroke(new BasicStroke(currentWidth,BasicStroke.CAP_ROUND,BasicStroke.JOIN_ROUND));
    291                         }
    292                         g2d.draw(currentPath);
    293                         g2d.setStroke(new BasicStroke(1));
    294 
    295                         currentPath = new GeneralPath();
    296                         currentColor = newColor;
    297                         currentWidth = newWidth;
    298                         currentDashed = newDash;
    299                         currentStroke = null;
    300                 }
    301         }
    302 
    303         /**
    304         * Draw the node as small rectangle with the given color.
    305         *
    306         * @param n  The node to draw.
    307         * @param color The color of the node.
    308         */
    309         public void drawNode(Node n, Color color, int size, int radius, boolean fill) {
    310                 if (isZoomOk(null) && size > 1) {
    311                         Point p = nc.getPoint(n.eastNorth);
    312                         if ((p.x < 0) || (p.y < 0) || (p.x > nc.getWidth())
    313                                         || (p.y > nc.getHeight()))
    314                                 return;
    315                         g.setColor(color);
    316                         if (fill) {
    317                                 g.fillRect(p.x - radius, p.y - radius, size, size);
    318                                 g.drawRect(p.x - radius, p.y - radius, size, size);
    319                         } else
    320                                 g.drawRect(p.x - radius, p.y - radius, size, size);
    321                 }
    322         }
    323 
    324         // NW 111106 Overridden from SimplePaintVisitor in josm-1.4-nw1
    325         // Shows areas before non-areas
    326         public void visitAll(DataSet data, Boolean virtual) {
    327                 getSettings(virtual);
    328                 untaggedColor = Main.pref.getColor(marktr("untagged"),Color.GRAY);
    329                 textColor = Main.pref.getColor (marktr("text"), Color.WHITE);
    330                 useRealWidth = Main.pref.getBoolean("mappaint.useRealWidth",false);
    331                 zoomLevelDisplay = Main.pref.getBoolean("mappaint.zoomLevelDisplay",false);
    332                 fillAreas = Main.pref.getBoolean("mappaint.fillareas", true);
    333                 fillAlpha = Math.min(255, Math.max(0, Integer.valueOf(Main.pref.getInteger("mappaint.fillalpha", 50))));
    334                 circum = Main.map.mapView.getScale()*100*Main.proj.scaleFactor()*40041455; // circumference of the earth in meter
    335                 styles = MapPaintStyles.getStyles();
    336                 orderFont = new Font(Main.pref.get("mappaint.font","Helvetica"), Font.PLAIN, Main.pref.getInteger("mappaint.fontsize", 8));
    337                 String currentLocale = Locale.getDefault().getLanguage();
    338                 regionalNameOrder = Main.pref.get("mappaint.nameOrder", "name:"+currentLocale+";name;int_name").split(";");
    339 
    340                 if (styles.hasAreas()) {
    341                         Collection<Way> noAreaWays = new LinkedList<Way>();
    342 
    343                         for (final OsmPrimitive osm : data.ways)
    344                                 if (!osm.incomplete && !osm.deleted && styles.isArea((Way)osm))
    345                                         osm.visit(this);
    346                                 else if (!osm.deleted && !osm.incomplete)
    347                                         noAreaWays.add((Way)osm);
    348 
    349                         for (final OsmPrimitive osm : noAreaWays)
    350                                 osm.visit(this);
    351                 }
    352                 else
    353                 {
    354                         for (final OsmPrimitive osm : data.ways)
    355                                 if (!osm.incomplete && !osm.deleted)
    356                                         osm.visit(this);
    357                 }
    358 
    359                 for (final OsmPrimitive osm : data.getSelected())
    360                         if (!osm.incomplete && !osm.deleted){
    361                                 osm.visit(this);
    362                         }
    363 
    364                 displaySegments();
    365 
    366                 for (final OsmPrimitive osm : data.nodes)
    367                         if (!osm.incomplete && !osm.deleted)
    368                                 osm.visit(this);
    369 
    370                 if (virtualNodeSize != 0)
    371                 {
    372                         currentColor = nodeColor;
    373                         for (final OsmPrimitive osm : data.ways)
    374                                 if (!osm.deleted)
    375                                         visitVirtual((Way)osm);
    376                         displaySegments(null);
    377                 }
    378         }
    379 
    380         /**
    381         * Draw a number of the order of the two consecutive nodes within the
    382         * parents way
    383         */
    384         protected void drawOrderNumber(Node n1, Node n2, int orderNumber) {
    385                 Point p1 = nc.getPoint(n1.eastNorth);
    386                 Point p2 = nc.getPoint(n2.eastNorth);
    387                 drawOrderNumber(p1, p2, orderNumber);
    388         }
     35    protected boolean useRealWidth;
     36    protected boolean zoomLevelDisplay;
     37    protected boolean fillAreas;
     38    protected int fillAlpha;
     39    protected Color untaggedColor;
     40    protected Color textColor;
     41    protected boolean currentDashed = false;
     42    protected int currentWidth = 0;
     43    protected Stroke currentStroke = null;
     44    protected Font orderFont;
     45    protected ElemStyles styles;
     46    protected double circum;
     47    protected String regionalNameOrder[];
     48
     49    protected boolean isZoomOk(ElemStyle e) {
     50        if (!zoomLevelDisplay) /* show everything if the user wishes so */
     51            return true;
     52
     53        if(e == null) /* the default for things that don't have a rule (show, if scale is smaller than 1500m) */
     54            return (circum < 1500);
     55
     56        // formula to calculate a map scale: natural size / map size = scale
     57        // example: 876000mm (876m as displayed) / 22mm (roughly estimated screen size of legend bar) = 39818
     58        //
     59        // so the exact "correcting value" below depends only on the screen size and resolution
     60        // XXX - do we need a Preference setting for this (if things vary widely)?
     61        return !(circum >= e.maxScale / 22 || circum < e.minScale / 22);
     62    }
     63
     64    /**
     65    * Draw a small rectangle.
     66    * White if selected (as always) or red otherwise.
     67    *
     68    * @param n The node to draw.
     69    */
     70    public void visit(Node n) {
     71        IconElemStyle nodeStyle = styles.get(n);
     72        if (nodeStyle != null && isZoomOk(nodeStyle))
     73            drawNode(n, nodeStyle.icon, nodeStyle.annotate);
     74        else if (n.selected)
     75            drawNode(n, selectedColor, selectedNodeSize, selectedNodeRadius, fillSelectedNode);
     76        else if (n.tagged)
     77            drawNode(n, nodeColor, taggedNodeSize, taggedNodeRadius, fillUnselectedNode);
     78        else
     79            drawNode(n, nodeColor, unselectedNodeSize, unselectedNodeRadius, fillUnselectedNode);
     80    }
     81
     82    /**
     83    * Draw a line for all segments, according to tags.
     84    * @param w The way to draw.
     85    */
     86    public void visit(Way w) {
     87        if(w.nodes.size() < 2)
     88            return;
     89        // show direction arrows, if draw.segment.relevant_directions_only is not set, the way is tagged with a direction key
     90        // (even if the tag is negated as in oneway=false) or the way is selected
     91        boolean showDirection = w.selected || ((!useRealWidth) && (showDirectionArrow
     92        && (!showRelevantDirectionsOnly || w.hasDirectionKeys)));
     93
     94        Color color = untaggedColor;
     95        int width = defaultSegmentWidth;
     96        int realWidth = 0; //the real width of the element in meters
     97        boolean dashed = false;
     98        ElemStyle wayStyle = styles.get(w);
     99
     100        if(!isZoomOk(wayStyle))
     101            return;
     102
     103        LineElemStyle l = null;
     104        if(wayStyle!=null)
     105        {
     106            Color areacolor = untaggedColor;
     107            boolean area = false;
     108            if(wayStyle instanceof LineElemStyle)
     109                l = (LineElemStyle)wayStyle;
     110            else if (wayStyle instanceof AreaElemStyle)
     111            {
     112                areacolor = ((AreaElemStyle)wayStyle).color;
     113                color = areacolor;
     114                l = ((AreaElemStyle)wayStyle).line;
     115                area = true;
     116            }
     117            if(l != null)
     118            {
     119                color = l.color;
     120                width = l.width;
     121                realWidth = l.realWidth;
     122                dashed = l.dashed;
     123            }
     124            if (area && fillAreas)
     125                drawWayAsArea(w, areacolor);
     126        }
     127
     128        if (realWidth > 0 && useRealWidth && !showDirection)
     129        {
     130            int tmpWidth = (int) (100 /  (float) (circum / realWidth));
     131            if (tmpWidth > width) width = tmpWidth;
     132        }
     133        if(w.selected)
     134            color = selectedColor;
     135
     136        Node lastN;
     137        if(l != null && l.overlays != null)
     138        {
     139            for(LineElemStyle s : l.overlays)
     140            {
     141                if(!s.over)
     142                {
     143                    lastN = null;
     144                    for(Node n : w.nodes)
     145                    {
     146                        if(lastN != null)
     147                        {
     148                            drawSeg(lastN, n, s.color != null  && !w.selected ? s.color : color,
     149                            false, s.getWidth(width), s.dashed);
     150                        }
     151                        lastN = n;
     152                    }
     153                }
     154            }
     155        }
     156
     157        lastN = null;
     158        for(Node n : w.nodes)
     159        {
     160            if(lastN != null)
     161                drawSeg(lastN, n, color, showDirection, width, dashed);
     162            lastN = n;
     163        }
     164
     165        if(l != null && l.overlays != null)
     166        {
     167            for(LineElemStyle s : l.overlays)
     168            {
     169                if(s.over)
     170                {
     171                    lastN = null;
     172                    for(Node n : w.nodes)
     173                    {
     174                        if(lastN != null)
     175                        {
     176                            drawSeg(lastN, n, s.color != null && !w.selected ? s.color : color,
     177                            false, s.getWidth(width), s.dashed);
     178                        }
     179                        lastN = n;
     180                    }
     181                }
     182            }
     183        }
     184
     185        if(showOrderNumber)
     186        {
     187            int orderNumber = 0;
     188            lastN = null;
     189            for(Node n : w.nodes)
     190            {
     191                if(lastN != null)
     192                {
     193                    orderNumber++;
     194                    drawOrderNumber(lastN, n, orderNumber);
     195                }
     196                lastN = n;
     197            }
     198        }
     199        displaySegments();
     200    }
     201
     202    public void visit(Relation e) {
     203        // relations are not (yet?) drawn.
     204    }
     205
     206    // This assumes that all segments are aligned in the same direction!
     207    protected void drawWayAsArea(Way w, Color color)
     208    {
     209        Polygon polygon = new Polygon();
     210
     211        for (Node n : w.nodes)
     212        {
     213            Point p = nc.getPoint(n.eastNorth);
     214            polygon.addPoint(p.x,p.y);
     215        }
     216
     217        Color mycolor = w.selected ? selectedColor : color;
     218        // set the opacity (alpha) level of the filled polygon
     219        g.setColor(new Color( mycolor.getRed(), mycolor.getGreen(), mycolor.getBlue(), fillAlpha));
     220
     221        g.fillPolygon(polygon);
     222    }
     223
     224    // NEW
     225    protected void drawNode(Node n, ImageIcon icon, boolean annotate) {
     226        Point p = nc.getPoint(n.eastNorth);
     227        if ((p.x < 0) || (p.y < 0) || (p.x > nc.getWidth()) || (p.y > nc.getHeight())) return;
     228        int w = icon.getIconWidth(), h=icon.getIconHeight();
     229        icon.paintIcon ( Main.map.mapView, g, p.x-w/2, p.y-h/2 );
     230        String name = getNodeName(n);
     231        if (name!=null && annotate)
     232        {
     233            g.setColor(textColor);
     234            Font defaultFont = g.getFont();
     235            g.setFont (orderFont);
     236            g.drawString (name, p.x+w/2+2, p.y+h/2+2);
     237            g.setFont(defaultFont);
     238        }
     239        if (n.selected)
     240        {
     241            g.setColor (  selectedColor );
     242            g.drawRect (p.x-w/2-2,p.y-w/2-2, w+4, h+4);
     243        }
     244    }
     245
     246    protected String getNodeName(Node n) {
     247        String name = null;
     248        if (n.keys != null) {
     249            for (int i = 0; i < regionalNameOrder.length; i++) {
     250                name = n.keys.get(regionalNameOrder[i]);
     251                if (name != null) break;
     252            }
     253        }
     254        return name;
     255    }
     256
     257    private void drawSeg(Node n1, Node n2, Color col, boolean showDirection, int width, boolean dashed) {
     258        if (col != currentColor || width != currentWidth || dashed != currentDashed) {
     259            displaySegments(col, width, dashed);
     260        }
     261        Point p1 = nc.getPoint(n1.eastNorth);
     262        Point p2 = nc.getPoint(n2.eastNorth);
     263
     264        if (!isSegmentVisible(p1, p2)) {
     265            return;
     266        }
     267        currentPath.moveTo(p1.x, p1.y);
     268        currentPath.lineTo(p2.x, p2.y);
     269
     270        if (showDirection) {
     271            double t = Math.atan2(p2.y-p1.y, p2.x-p1.x) + Math.PI;
     272            currentPath.lineTo((int)(p2.x + 10*Math.cos(t-PHI)), (int)(p2.y + 10*Math.sin(t-PHI)));
     273            currentPath.moveTo((int)(p2.x + 10*Math.cos(t+PHI)), (int)(p2.y + 10*Math.sin(t+PHI)));
     274            currentPath.lineTo(p2.x, p2.y);
     275        }
     276    }
     277
     278    protected void displaySegments() {
     279        displaySegments(null, 0, false);
     280    }
     281
     282    protected void displaySegments(Color newColor, int newWidth, boolean newDash) {
     283        if (currentPath != null) {
     284            Graphics2D g2d = (Graphics2D)g;
     285            g2d.setColor(inactive ? inactiveColor : currentColor);
     286            if (currentStroke == null) {
     287                if (currentDashed)
     288                    g2d.setStroke(new BasicStroke(currentWidth,BasicStroke.CAP_BUTT,BasicStroke.JOIN_ROUND,0,new float[] {9},0));
     289                else
     290                    g2d.setStroke(new BasicStroke(currentWidth,BasicStroke.CAP_ROUND,BasicStroke.JOIN_ROUND));
     291            }
     292            g2d.draw(currentPath);
     293            g2d.setStroke(new BasicStroke(1));
     294
     295            currentPath = new GeneralPath();
     296            currentColor = newColor;
     297            currentWidth = newWidth;
     298            currentDashed = newDash;
     299            currentStroke = null;
     300        }
     301    }
     302
     303    /**
     304    * Draw the node as small rectangle with the given color.
     305    *
     306    * @param n  The node to draw.
     307    * @param color The color of the node.
     308    */
     309    public void drawNode(Node n, Color color, int size, int radius, boolean fill) {
     310        if (isZoomOk(null) && size > 1) {
     311            Point p = nc.getPoint(n.eastNorth);
     312            if ((p.x < 0) || (p.y < 0) || (p.x > nc.getWidth())
     313                    || (p.y > nc.getHeight()))
     314                return;
     315            g.setColor(color);
     316            if (fill) {
     317                g.fillRect(p.x - radius, p.y - radius, size, size);
     318                g.drawRect(p.x - radius, p.y - radius, size, size);
     319            } else
     320                g.drawRect(p.x - radius, p.y - radius, size, size);
     321        }
     322    }
     323
     324    // NW 111106 Overridden from SimplePaintVisitor in josm-1.4-nw1
     325    // Shows areas before non-areas
     326    public void visitAll(DataSet data, Boolean virtual) {
     327        getSettings(virtual);
     328        untaggedColor = Main.pref.getColor(marktr("untagged"),Color.GRAY);
     329        textColor = Main.pref.getColor (marktr("text"), Color.WHITE);
     330        useRealWidth = Main.pref.getBoolean("mappaint.useRealWidth",false);
     331        zoomLevelDisplay = Main.pref.getBoolean("mappaint.zoomLevelDisplay",false);
     332        fillAreas = Main.pref.getBoolean("mappaint.fillareas", true);
     333        fillAlpha = Math.min(255, Math.max(0, Integer.valueOf(Main.pref.getInteger("mappaint.fillalpha", 50))));
     334        circum = Main.map.mapView.getScale()*100*Main.proj.scaleFactor()*40041455; // circumference of the earth in meter
     335        styles = MapPaintStyles.getStyles();
     336        orderFont = new Font(Main.pref.get("mappaint.font","Helvetica"), Font.PLAIN, Main.pref.getInteger("mappaint.fontsize", 8));
     337        String currentLocale = Locale.getDefault().getLanguage();
     338        regionalNameOrder = Main.pref.get("mappaint.nameOrder", "name:"+currentLocale+";name;int_name").split(";");
     339
     340        if (styles.hasAreas()) {
     341            Collection<Way> noAreaWays = new LinkedList<Way>();
     342
     343            for (final OsmPrimitive osm : data.ways)
     344                if (!osm.incomplete && !osm.deleted && styles.isArea((Way)osm))
     345                    osm.visit(this);
     346                else if (!osm.deleted && !osm.incomplete)
     347                    noAreaWays.add((Way)osm);
     348
     349            for (final OsmPrimitive osm : noAreaWays)
     350                osm.visit(this);
     351        }
     352        else
     353        {
     354            for (final OsmPrimitive osm : data.ways)
     355                if (!osm.incomplete && !osm.deleted)
     356                    osm.visit(this);
     357        }
     358
     359        for (final OsmPrimitive osm : data.getSelected())
     360            if (!osm.incomplete && !osm.deleted){
     361                osm.visit(this);
     362            }
     363
     364        displaySegments();
     365
     366        for (final OsmPrimitive osm : data.nodes)
     367            if (!osm.incomplete && !osm.deleted)
     368                osm.visit(this);
     369
     370        if (virtualNodeSize != 0)
     371        {
     372            currentColor = nodeColor;
     373            for (final OsmPrimitive osm : data.ways)
     374                if (!osm.deleted)
     375                    visitVirtual((Way)osm);
     376            displaySegments(null);
     377        }
     378    }
     379
     380    /**
     381    * Draw a number of the order of the two consecutive nodes within the
     382    * parents way
     383    */
     384    protected void drawOrderNumber(Node n1, Node n2, int orderNumber) {
     385        Point p1 = nc.getPoint(n1.eastNorth);
     386        Point p2 = nc.getPoint(n2.eastNorth);
     387        drawOrderNumber(p1, p2, orderNumber);
     388    }
    389389}
  • trunk/src/org/openstreetmap/josm/data/osm/visitor/MergeVisitor.java

    r627 r1169  
    1919 * A visitor that get a data set at construction time and merge every visited object
    2020 * into it.
    21  * 
     21 *
    2222 * @author imi
    2323 */
    2424public class MergeVisitor implements Visitor {
    2525
    26         /**
    27          * Map from primitives in the database to visited primitives. (Attention: The other way
    28         * round than merged)
    29         */
    30         public Map<OsmPrimitive, OsmPrimitive> conflicts = new HashMap<OsmPrimitive, OsmPrimitive>();
    31 
    32         private final DataSet ds;
    33         private final DataSet mergeds;
    34 
    35         private final HashMap<Long, Node> nodeshash = new HashMap<Long, Node>();
    36         private final HashMap<Long, Way> wayshash = new HashMap<Long, Way>();
    37         private final HashMap<Long, Relation> relshash = new HashMap<Long, Relation>();
    38 
    39         /**
    40         * A list of all primitives that got replaced with other primitives.
    41         * Key is the primitives in the other's dataset and the value is the one that is now
    42         * in ds.nodes instead.
    43         */
    44         private final Map<OsmPrimitive, OsmPrimitive> merged
    45                 = new HashMap<OsmPrimitive, OsmPrimitive>();
    46 
    47         public MergeVisitor(DataSet ds, DataSet mergeds) {
    48                 this.ds = ds;
    49                 this.mergeds = mergeds;
    50 
    51                 for (Node n : ds.nodes) if (n.id != 0) nodeshash.put(n.id, n);
    52                 for (Way w : ds.ways) if (w.id != 0) wayshash.put(w.id, w);
    53                 for (Relation r : ds.relations) if (r.id != 0) relshash.put(r.id, r);
    54         }
    55 
    56         private <P extends OsmPrimitive> void genMerge(P other,
    57                         Collection<P> myprims, Collection<P> mergeprims,
    58                         HashMap<Long, P> primhash) {
    59                 // 1. Try to find an identical prim with the same id.
    60                 if (mergeAfterId(myprims, primhash, other))
    61                         return;
    62 
    63                 // 2. Try to find a prim we can merge with the prim from the other ds.
    64                 for (P my : myprims) {
    65                         // LinkedList.contains calls equal, and OsmPrimitive.equal
    66                         // compares just the id.
    67                         if (match(my, other) && !mergeprims.contains(my)) {
    68                                 merged.put(other, my);
    69                                 mergeCommon(my, other);
    70                                 return;
    71                         }
    72                 }
    73 
    74                 // 3. No idea how to merge that.  Simply add it unchanged.
    75                 myprims.add(other);
    76         }
    77 
    78         public void visit(Node other) {
    79                 genMerge(other, ds.nodes, mergeds.nodes, nodeshash);
    80         }
    81 
    82         public void visit(Way other) {
    83                 fixWay(other);
    84                 genMerge(other, ds.ways, mergeds.ways, wayshash);
    85         }
    86 
    87         public void visit(Relation other) {
    88                 fixRelation(other);
    89                 genMerge(other, ds.relations, mergeds.relations, relshash);
    90         }
    91 
    92         /**
    93         * Postprocess the dataset and fix all merged references to point to the actual
    94         * data.
    95         */
    96         public void fixReferences() {
    97                 for (Way w : ds.ways) fixWay(w);
    98                 for (Relation r : ds.relations) fixRelation(r);
    99                 for (OsmPrimitive osm : conflicts.values())
    100                         if (osm instanceof Way)
    101                                 fixWay((Way)osm);
    102                         else if (osm instanceof Relation)
    103                                 fixRelation((Relation) osm);
    104         }
    105 
    106         private void fixWay(Way w) {
    107             boolean replacedSomething = false;
    108             LinkedList<Node> newNodes = new LinkedList<Node>();
    109             for (Node n : w.nodes) {
    110                 Node otherN = (Node) merged.get(n);
    111                 newNodes.add(otherN == null ? n : otherN);
    112                 if (otherN != null)
    113                         replacedSomething = true;
    114             }
    115             if (replacedSomething) {
    116                 w.nodes.clear();
    117                 w.nodes.addAll(newNodes);
    118                 }
    119     }
    120 
    121         private void fixRelation(Relation r) {
    122             boolean replacedSomething = false;
    123             LinkedList<RelationMember> newMembers = new LinkedList<RelationMember>();
    124             for (RelationMember m : r.members) {
    125                 OsmPrimitive otherP = merged.get(m.member);
    126                         if (otherP == null) {
    127                                 newMembers.add(m);
    128                         } else {
    129                                 RelationMember mnew = new RelationMember(m);
    130                                 mnew.member = otherP;
    131                                 newMembers.add(mnew);
    132                         replacedSomething = true;
    133                         }
    134             }
    135             if (replacedSomething) {
    136                 r.members.clear();
    137                 r.members.addAll(newMembers);
    138                 }
    139         }
    140 
    141         private static <P extends OsmPrimitive> boolean match(P p1, P p2) {
    142                 if ((p1.id == 0 || p2.id == 0) && !p1.incomplete && !p2.incomplete) {
    143                         return realMatch(p1, p2);
    144                 }
    145                 return p1.id == p2.id;
    146         }
    147 
    148         /** @return true if the prims have pretty much the same data, i.e. the
    149         * same position, the same members, ...
    150         */
    151         // Java cannot dispatch on generics...
    152         private static boolean realMatch(OsmPrimitive p1, OsmPrimitive p2) {
    153                 if (p1 instanceof Node && p2 instanceof Node) {
    154                         return realMatch((Node) p1, (Node) p2);
    155                 } else if (p1 instanceof Way && p2 instanceof Way) {
    156                         return realMatch((Way) p1, (Way) p2);
    157                 } else if (p1 instanceof Relation && p2 instanceof Relation) {
    158                         return realMatch((Relation) p1, (Relation) p2);
    159                 } else {
    160                         throw new RuntimeException("arguments have unknown type");
    161                 }
    162         }
    163 
    164         private static boolean realMatch(Node n1, Node n2) {
    165                 return n1.coor.equalsEpsilon(n2.coor);
    166         }
    167 
    168         private static boolean realMatch(Way w1, Way w2) {
    169                 if (w1.nodes.size() != w2.nodes.size())
    170                         return false;
    171                 Iterator<Node> it = w1.nodes.iterator();
    172                 for (Node n : w2.nodes)
    173                         if (!match(n, it.next()))
    174                                 return false;
    175                 return true;
    176         }
    177 
    178         private static boolean realMatch(Relation w1, Relation w2) {
    179                 // FIXME this is not perfect yet...
    180                 if (w1.members.size() != w2.members.size())
    181                         return false;
    182                 for (RelationMember em : w1.members) {
    183                         if (!w2.members.contains(em)) {
    184                                 return false;
    185                         }
    186                 }
    187                 return true;
    188         }
    189 
    190         /**
    191         * Merge the common parts of an osm primitive.
    192         * @param my The object, the information gets merged into
    193         * @param other The object, the information gets merged from
    194         */
    195         private void mergeCommon(OsmPrimitive my, OsmPrimitive other) {
    196                 if (other.deleted)
    197                         my.delete(true);
    198                 if (my.id == 0 || !my.modified || other.modified) {
    199                         if (my.id == 0 && other.id != 0) {
    200                                 my.id = other.id;
    201                                 my.modified = other.modified; // match a new node
    202                         } else if (my.id != 0 && other.id != 0 && other.modified)
    203                                 my.modified = true;
    204                 }
    205                 if (other.keys == null)
    206                         return;
    207                 if (my.keySet().containsAll(other.keys.keySet()))
    208                         return;
    209                 if (my.keys == null)
    210                         my.keys = other.keys;
    211                 else
    212                         my.keys.putAll(other.keys);
    213                
    214                 my.modified = true;
    215         }
    216 
    217         /**
    218         * @return <code>true</code>, if no merge is needed or merge is performed already.
    219         */
    220         private <P extends OsmPrimitive> boolean mergeAfterId(
    221                         Collection<P> primitives, HashMap<Long, P> hash, P other) {
    222                 // Fast-path merging of identical objects
    223                 if (hash.containsKey(other.id)) {
    224                         P my = hash.get(other.id);
    225                         if (my.realEqual(other, true)) {
    226                                 merged.put(other, my);
    227                                 return true;
    228                         }
    229                 }
    230 
    231                 for (P my : primitives) {
    232                         if (my.realEqual(other, false)) {
    233                                 merged.put(other, my);
    234                                 return true; // no merge needed.
    235                         }
    236                         if (my.realEqual(other, true)) {
    237                                 Date myd = my.timestamp == null ? new Date(0) : my.getTimestamp();
    238                                 Date otherd = other.timestamp == null ? new Date(0) : other.getTimestamp();
    239 
    240                                 // they differ in modified/timestamp combination only. Auto-resolve it.
    241                                 merged.put(other, my);
    242                                 if (myd.before(otherd)) {
    243                                         my.modified = other.modified;
    244                                         my.timestamp = other.timestamp;
    245                                 }
    246                                 return true; // merge done.
    247                         }
    248                         if (my.id == other.id && my.id != 0) {
    249                                 Date myd = my.timestamp == null ? new Date(0) : my.getTimestamp();
    250                                 Date otherd = other.timestamp == null ? new Date(0) : other.getTimestamp();
    251 
    252                                 if (my.incomplete || other.incomplete) {
    253                                         if (my.incomplete) {
    254                                                 my.cloneFrom(other);
    255                                         }
    256                                 } else if (my.modified && other.modified) {
    257                                         conflicts.put(my, other);
    258                                 } else if (!my.modified && !other.modified) {
    259                                         if (myd.before(otherd)) {
    260                                                 my.cloneFrom(other);
    261                                         }
    262                                 } else if (other.modified) {
    263                                         if (myd.after(otherd)) {
    264                                                 conflicts.put(my, other);
    265                                         } else {
    266                                                 my.cloneFrom(other);
    267                                         }
    268                                 } else if (my.modified) {
    269                                         if (myd.before(otherd)) {
    270                                                 conflicts.put(my, other);
    271                                         }
    272                                 }
    273                                 merged.put(other, my);
    274                                 return true;
    275                         }
    276                 }
    277                 return false;
    278         }
     26    /**
     27     * Map from primitives in the database to visited primitives. (Attention: The other way
     28    * round than merged)
     29    */
     30    public Map<OsmPrimitive, OsmPrimitive> conflicts = new HashMap<OsmPrimitive, OsmPrimitive>();
     31
     32    private final DataSet ds;
     33    private final DataSet mergeds;
     34
     35    private final HashMap<Long, Node> nodeshash = new HashMap<Long, Node>();
     36    private final HashMap<Long, Way> wayshash = new HashMap<Long, Way>();
     37    private final HashMap<Long, Relation> relshash = new HashMap<Long, Relation>();
     38
     39    /**
     40    * A list of all primitives that got replaced with other primitives.
     41    * Key is the primitives in the other's dataset and the value is the one that is now
     42    * in ds.nodes instead.
     43    */
     44    private final Map<OsmPrimitive, OsmPrimitive> merged
     45        = new HashMap<OsmPrimitive, OsmPrimitive>();
     46
     47    public MergeVisitor(DataSet ds, DataSet mergeds) {
     48        this.ds = ds;
     49        this.mergeds = mergeds;
     50
     51        for (Node n : ds.nodes) if (n.id != 0) nodeshash.put(n.id, n);
     52        for (Way w : ds.ways) if (w.id != 0) wayshash.put(w.id, w);
     53        for (Relation r : ds.relations) if (r.id != 0) relshash.put(r.id, r);
     54    }
     55
     56    private <P extends OsmPrimitive> void genMerge(P other,
     57            Collection<P> myprims, Collection<P> mergeprims,
     58            HashMap<Long, P> primhash) {
     59        // 1. Try to find an identical prim with the same id.
     60        if (mergeAfterId(myprims, primhash, other))
     61            return;
     62
     63        // 2. Try to find a prim we can merge with the prim from the other ds.
     64        for (P my : myprims) {
     65            // LinkedList.contains calls equal, and OsmPrimitive.equal
     66            // compares just the id.
     67            if (match(my, other) && !mergeprims.contains(my)) {
     68                merged.put(other, my);
     69                mergeCommon(my, other);
     70                return;
     71            }
     72        }
     73
     74        // 3. No idea how to merge that.  Simply add it unchanged.
     75        myprims.add(other);
     76    }
     77
     78    public void visit(Node other) {
     79        genMerge(other, ds.nodes, mergeds.nodes, nodeshash);
     80    }
     81
     82    public void visit(Way other) {
     83        fixWay(other);
     84        genMerge(other, ds.ways, mergeds.ways, wayshash);
     85    }
     86
     87    public void visit(Relation other) {
     88        fixRelation(other);
     89        genMerge(other, ds.relations, mergeds.relations, relshash);
     90    }
     91
     92    /**
     93    * Postprocess the dataset and fix all merged references to point to the actual
     94    * data.
     95    */
     96    public void fixReferences() {
     97        for (Way w : ds.ways) fixWay(w);
     98        for (Relation r : ds.relations) fixRelation(r);
     99        for (OsmPrimitive osm : conflicts.values())
     100            if (osm instanceof Way)
     101                fixWay((Way)osm);
     102            else if (osm instanceof Relation)
     103                fixRelation((Relation) osm);
     104    }
     105
     106    private void fixWay(Way w) {
     107        boolean replacedSomething = false;
     108        LinkedList<Node> newNodes = new LinkedList<Node>();
     109        for (Node n : w.nodes) {
     110            Node otherN = (Node) merged.get(n);
     111            newNodes.add(otherN == null ? n : otherN);
     112            if (otherN != null)
     113                replacedSomething = true;
     114        }
     115        if (replacedSomething) {
     116            w.nodes.clear();
     117            w.nodes.addAll(newNodes);
     118        }
     119    }
     120
     121    private void fixRelation(Relation r) {
     122        boolean replacedSomething = false;
     123        LinkedList<RelationMember> newMembers = new LinkedList<RelationMember>();
     124        for (RelationMember m : r.members) {
     125            OsmPrimitive otherP = merged.get(m.member);
     126            if (otherP == null) {
     127                newMembers.add(m);
     128            } else {
     129                RelationMember mnew = new RelationMember(m);
     130                mnew.member = otherP;
     131                newMembers.add(mnew);
     132                replacedSomething = true;
     133            }
     134        }
     135        if (replacedSomething) {
     136            r.members.clear();
     137            r.members.addAll(newMembers);
     138        }
     139    }
     140
     141    private static <P extends OsmPrimitive> boolean match(P p1, P p2) {
     142        if ((p1.id == 0 || p2.id == 0) && !p1.incomplete && !p2.incomplete) {
     143            return realMatch(p1, p2);
     144        }
     145        return p1.id == p2.id;
     146    }
     147
     148    /** @return true if the prims have pretty much the same data, i.e. the
     149    * same position, the same members, ...
     150    */
     151    // Java cannot dispatch on generics...
     152    private static boolean realMatch(OsmPrimitive p1, OsmPrimitive p2) {
     153        if (p1 instanceof Node && p2 instanceof Node) {
     154            return realMatch((Node) p1, (Node) p2);
     155        } else if (p1 instanceof Way && p2 instanceof Way) {
     156            return realMatch((Way) p1, (Way) p2);
     157        } else if (p1 instanceof Relation && p2 instanceof Relation) {
     158            return realMatch((Relation) p1, (Relation) p2);
     159        } else {
     160            throw new RuntimeException("arguments have unknown type");
     161        }
     162    }
     163
     164    private static boolean realMatch(Node n1, Node n2) {
     165        return n1.coor.equalsEpsilon(n2.coor);
     166    }
     167
     168    private static boolean realMatch(Way w1, Way w2) {
     169        if (w1.nodes.size() != w2.nodes.size())
     170            return false;
     171        Iterator<Node> it = w1.nodes.iterator();
     172        for (Node n : w2.nodes)
     173            if (!match(n, it.next()))
     174                return false;
     175        return true;
     176    }
     177
     178    private static boolean realMatch(Relation w1, Relation w2) {
     179        // FIXME this is not perfect yet...
     180        if (w1.members.size() != w2.members.size())
     181            return false;
     182        for (RelationMember em : w1.members) {
     183            if (!w2.members.contains(em)) {
     184                return false;
     185            }
     186        }
     187        return true;
     188    }
     189
     190    /**
     191    * Merge the common parts of an osm primitive.
     192    * @param my The object, the information gets merged into
     193    * @param other The object, the information gets merged from
     194    */
     195    private void mergeCommon(OsmPrimitive my, OsmPrimitive other) {
     196        if (other.deleted)
     197            my.delete(true);
     198        if (my.id == 0 || !my.modified || other.modified) {
     199            if (my.id == 0 && other.id != 0) {
     200                my.id = other.id;
     201                my.modified = other.modified; // match a new node
     202            } else if (my.id != 0 && other.id != 0 && other.modified)
     203                my.modified = true;
     204        }
     205        if (other.keys == null)
     206            return;
     207        if (my.keySet().containsAll(other.keys.keySet()))
     208            return;
     209        if (my.keys == null)
     210            my.keys = other.keys;
     211        else
     212            my.keys.putAll(other.keys);
     213
     214        my.modified = true;
     215    }
     216
     217    /**
     218    * @return <code>true</code>, if no merge is needed or merge is performed already.
     219    */
     220    private <P extends OsmPrimitive> boolean mergeAfterId(
     221            Collection<P> primitives, HashMap<Long, P> hash, P other) {
     222        // Fast-path merging of identical objects
     223        if (hash.containsKey(other.id)) {
     224            P my = hash.get(other.id);
     225            if (my.realEqual(other, true)) {
     226                merged.put(other, my);
     227                return true;
     228            }
     229        }
     230
     231        for (P my : primitives) {
     232            if (my.realEqual(other, false)) {
     233                merged.put(other, my);
     234                return true; // no merge needed.
     235            }
     236            if (my.realEqual(other, true)) {
     237                Date myd = my.timestamp == null ? new Date(0) : my.getTimestamp();
     238                Date otherd = other.timestamp == null ? new Date(0) : other.getTimestamp();
     239
     240                // they differ in modified/timestamp combination only. Auto-resolve it.
     241                merged.put(other, my);
     242                if (myd.before(otherd)) {
     243                    my.modified = other.modified;
     244                    my.timestamp = other.timestamp;
     245                }
     246                return true; // merge done.
     247            }
     248            if (my.id == other.id && my.id != 0) {
     249                Date myd = my.timestamp == null ? new Date(0) : my.getTimestamp();
     250                Date otherd = other.timestamp == null ? new Date(0) : other.getTimestamp();
     251
     252                if (my.incomplete || other.incomplete) {
     253                    if (my.incomplete) {
     254                        my.cloneFrom(other);
     255                    }
     256                } else if (my.modified && other.modified) {
     257                    conflicts.put(my, other);
     258                } else if (!my.modified && !other.modified) {
     259                    if (myd.before(otherd)) {
     260                        my.cloneFrom(other);
     261                    }
     262                } else if (other.modified) {
     263                    if (myd.after(otherd)) {
     264                        conflicts.put(my, other);
     265                    } else {
     266                        my.cloneFrom(other);
     267                    }
     268                } else if (my.modified) {
     269                    if (myd.before(otherd)) {
     270                        conflicts.put(my, other);
     271                    }
     272                }
     273                merged.put(other, my);
     274                return true;
     275            }
     276        }
     277        return false;
     278    }
    279279}
  • trunk/src/org/openstreetmap/josm/data/osm/visitor/NameVisitor.java

    r756 r1169  
    1717/**
    1818 * Able to create a name and an icon for each data element.
    19  * 
     19 *
    2020 * @author imi
    2121 */
    2222public class NameVisitor implements Visitor {
    2323
    24         /**
    25          * The name of the item class
    26          */
    27         public String className;
    28         public String classNamePlural;
    29         /**
    30          * The name of this item.
    31          */
    32         public String name;
    33         /**
    34          * The icon of this item.
    35          */
    36         public Icon icon;
    37        
    38         /**
    39          * If the node has a name-key or id-key, this is displayed. If not, (lat,lon)
    40          * is displayed.
    41          */
    42         public void visit(Node n) {
    43                 name = n.getName();
    44                 addId(n);
    45                 icon = ImageProvider.get("data", "node");
    46                 className = "node";
    47                 classNamePlural = trn("node", "nodes", 2);
    48         }
     24    /**
     25     * The name of the item class
     26     */
     27    public String className;
     28    public String classNamePlural;
     29    /**
     30     * The name of this item.
     31     */
     32    public String name;
     33    /**
     34     * The icon of this item.
     35     */
     36    public Icon icon;
    4937
    50         /**
    51          * If the way has a name-key or id-key, this is displayed. If not, (x nodes)
    52          * is displayed with x being the number of nodes in the way.
    53          */
    54         public void visit(Way w) {
    55                 name = w.getName();
    56                 addId(w);
    57                 icon = ImageProvider.get("data", "way");
    58                 className = "way";
    59                 classNamePlural = trn("way", "ways", 2);
    60         }
    61        
    62         /**
    63          */
    64         public void visit(Relation e) {
    65                 name = e.getName();
    66                 addId(e);
    67                 icon = ImageProvider.get("data", "relation");
    68                 className = "relation";
    69                 classNamePlural = trn("relation", "relations", 2);
    70         }
    71        
    72         public JLabel toLabel() {
    73                 return new JLabel(name, icon, JLabel.HORIZONTAL);
    74         }
     38    /**
     39     * If the node has a name-key or id-key, this is displayed. If not, (lat,lon)
     40     * is displayed.
     41     */
     42    public void visit(Node n) {
     43        name = n.getName();
     44        addId(n);
     45        icon = ImageProvider.get("data", "node");
     46        className = "node";
     47        classNamePlural = trn("node", "nodes", 2);
     48    }
     49
     50    /**
     51     * If the way has a name-key or id-key, this is displayed. If not, (x nodes)
     52     * is displayed with x being the number of nodes in the way.
     53     */
     54    public void visit(Way w) {
     55        name = w.getName();
     56        addId(w);
     57        icon = ImageProvider.get("data", "way");
     58        className = "way";
     59        classNamePlural = trn("way", "ways", 2);
     60    }
     61
     62    /**
     63     */
     64    public void visit(Relation e) {
     65        name = e.getName();
     66        addId(e);
     67        icon = ImageProvider.get("data", "relation");
     68        className = "relation";
     69        classNamePlural = trn("relation", "relations", 2);
     70    }
     71
     72    public JLabel toLabel() {
     73        return new JLabel(name, icon, JLabel.HORIZONTAL);
     74    }
    7575
    7676
    77         private void addId(OsmPrimitive osm) {
    78             if (Main.pref.getBoolean("osm-primitives.showid"))
    79                         name += tr(" [id: {0}]", osm.id);
     77    private void addId(OsmPrimitive osm) {
     78        if (Main.pref.getBoolean("osm-primitives.showid"))
     79            name += tr(" [id: {0}]", osm.id);
    8080    }
    8181}
  • trunk/src/org/openstreetmap/josm/data/osm/visitor/SimplePaintVisitor.java

    r999 r1169  
    3232public class SimplePaintVisitor implements Visitor {
    3333
    34         public final static Color darkerblue = new Color(0,0,96);
    35         public final static Color darkblue = new Color(0,0,128);
    36         public final static Color darkgreen = new Color(0,128,0);
    37         public final static Color teal = new Color(0,128,128);
    38 
    39         /**
    40         * The environment to paint to.
    41         */
    42         protected Graphics g;
    43         /**
    44         * MapView to get screen coordinates.
    45         */
    46         protected NavigatableComponent nc;
    47 
    48         public boolean inactive;
    49 
    50         protected static final double PHI = Math.toRadians(20);
    51 
    52         /**
    53         * Preferences
    54         */
    55         protected Color inactiveColor;
    56         protected Color selectedColor;
    57         protected Color nodeColor;
    58         protected Color dfltWayColor;
    59         protected Color relationColor;
    60         protected Color untaggedWayColor;
    61         protected Color incompleteColor;
    62         protected Color backgroundColor;
    63         protected boolean showDirectionArrow;
    64         protected boolean showRelevantDirectionsOnly;
    65         protected boolean showOrderNumber;
    66         protected boolean fillSelectedNode;
    67         protected boolean fillUnselectedNode;
    68         protected int selectedNodeRadius;
    69         protected int unselectedNodeRadius;
    70         protected int selectedNodeSize;
    71         protected int unselectedNodeSize;
    72         protected int defaultSegmentWidth;
    73         protected int virtualNodeSize;
    74         protected int virtualNodeSpace;
    75         protected int segmentNumberSpace;
    76         protected int taggedNodeRadius;
    77         protected int taggedNodeSize;
    78 
    79         /**
    80         * Draw subsequent segments of same color as one Path
    81         */
    82         protected Color currentColor = null;
    83         protected GeneralPath currentPath = new GeneralPath();
    84 
    85         Rectangle bbox = new Rectangle();
    86 
    87         protected void getSettings(Boolean virtual) {
    88                 inactiveColor = Main.pref.getColor(marktr("inactive"), Color.DARK_GRAY);
    89                 selectedColor = Main.pref.getColor(marktr("selected"), Color.WHITE);
    90                 nodeColor = Main.pref.getColor(marktr("node"), Color.RED);
    91                 dfltWayColor = Main.pref.getColor(marktr("way"), darkblue);
    92                 relationColor = Main.pref.getColor(marktr("relation"), teal);
    93                 untaggedWayColor = Main.pref.getColor(marktr("untagged way"), darkgreen);
    94                 incompleteColor = Main.pref.getColor(marktr("incomplete way"), darkerblue);
    95                 backgroundColor = Main.pref.getColor(marktr("background"), Color.BLACK);
    96                 showDirectionArrow = Main.pref.getBoolean("draw.segment.direction");
    97                 showRelevantDirectionsOnly = Main.pref.getBoolean("draw.segment.relevant_directions_only");
    98                 showOrderNumber = Main.pref.getBoolean("draw.segment.order_number");
    99                 selectedNodeRadius = Main.pref.getInteger("mappaint.node.selected-size", 5) / 2;
    100                 selectedNodeSize = selectedNodeRadius * 2;
    101                 unselectedNodeRadius = Main.pref.getInteger("mappaint.node.unselected-size", 3) / 2;
    102                 unselectedNodeSize = unselectedNodeRadius * 2;
    103                 taggedNodeRadius = Main.pref.getInteger("mappaint.node.tagged-size", 5) / 2;
    104                 taggedNodeSize = taggedNodeRadius * 2;
    105                 defaultSegmentWidth = Main.pref.getInteger("mappaint.segment.default-width", 2);
    106                 fillSelectedNode = Main.pref.getBoolean("mappaint.node.fill-selected", true);
    107                 fillUnselectedNode = Main.pref.getBoolean("mappaint.node.fill-unselected", false);
    108                 virtualNodeSize = virtual ? Main.pref.getInteger("mappaint.node.virtual-size", 8) / 2 : 0;
    109                 virtualNodeSpace = Main.pref.getInteger("mappaint.node.virtual-space", 70);
    110                 segmentNumberSpace = Main.pref.getInteger("mappaint.segmentnumber.space", 40);
    111 
    112                 ((Graphics2D)g).setRenderingHint(RenderingHints.KEY_ANTIALIASING,
    113                         Main.pref.getBoolean("mappaint.use-antialiasing", false) ?
    114                         RenderingHints.VALUE_ANTIALIAS_ON : RenderingHints.VALUE_ANTIALIAS_OFF);
    115         }
    116 
    117         public void visitAll(DataSet data, Boolean virtual) {
    118                 getSettings(virtual);
    119                 // draw tagged ways first, then untagged ways. takes
    120                 // time to iterate through list twice, OTOH does not
    121                 // require changing the colour while painting...
    122                 for (final OsmPrimitive osm : data.relations)
    123                         if (!osm.deleted && !osm.selected)
    124                                 osm.visit(this);
    125 
    126                 for (final OsmPrimitive osm : data.ways)
    127                         if (!osm.deleted && !osm.selected && osm.tagged)
    128                                 osm.visit(this);
    129                 displaySegments();
    130 
    131                 for (final OsmPrimitive osm : data.ways)
    132                         if (!osm.deleted && !osm.selected && !osm.tagged)
    133                                 osm.visit(this);
    134                 displaySegments();
    135 
    136                 for (final OsmPrimitive osm : data.getSelected())
    137                         if (!osm.deleted)
    138                                 osm.visit(this);
    139                 displaySegments();
    140 
    141                 for (final OsmPrimitive osm : data.nodes)
    142                         if (!osm.deleted && !osm.selected)
    143                                 osm.visit(this);
    144                 if(virtualNodeSize != 0)
    145                 {
    146                         currentColor = nodeColor;
    147                         for (final OsmPrimitive osm : data.ways)
    148                                 if (!osm.deleted)
    149                                         visitVirtual((Way)osm);
    150                         displaySegments();
    151                 }
    152         }
    153 
    154         /**
    155         * Draw a small rectangle.
    156         * White if selected (as always) or red otherwise.
    157         *
    158         * @param n The node to draw.
    159         */
    160         public void visit(Node n) {
    161                 if (n.incomplete) return;
    162 
    163                 if (inactive)
    164                         drawNode(n, inactiveColor, unselectedNodeSize, unselectedNodeRadius, fillUnselectedNode);
    165                 else if (n.selected)
    166                         drawNode(n, selectedColor, selectedNodeSize, selectedNodeRadius, fillSelectedNode);
    167                 else if(n.tagged)
    168                         drawNode(n, nodeColor, taggedNodeSize, taggedNodeRadius, fillUnselectedNode);
    169                 else
    170                         drawNode(n, nodeColor, unselectedNodeSize, unselectedNodeRadius, fillUnselectedNode);
    171         }
    172 
    173         public static Boolean isLargeSegment(Point p1, Point p2, int space)
    174         {
    175                 int xd = p1.x-p2.x; if(xd < 0) xd = -xd;
    176                 int yd = p1.y-p2.y; if(yd < 0) yd = -yd;
    177                 return (xd+yd > space);
    178         }
    179 
    180         public void visitVirtual(Way w) {
    181                 Iterator<Node> it = w.nodes.iterator();
    182                 if (it.hasNext()) {
    183                         Point lastP = nc.getPoint(it.next().eastNorth);
    184                         while(it.hasNext())
    185                         {
    186                                 Point p = nc.getPoint(it.next().eastNorth);
    187                                 if(isSegmentVisible(lastP, p) && isLargeSegment(lastP, p, virtualNodeSpace))
    188                                 {
    189                                         int x = (p.x+lastP.x)/2;
    190                                         int y = (p.y+lastP.y)/2;
    191                                         currentPath.moveTo(x-virtualNodeSize, y);
    192                                         currentPath.lineTo(x+virtualNodeSize, y);
    193                                         currentPath.moveTo(x, y-virtualNodeSize);
    194                                         currentPath.lineTo(x, y+virtualNodeSize);
    195                                 }
    196                                 lastP = p;
    197                         }
    198                 }
    199         }
    200 
    201         /**
    202         * Draw a darkblue line for all segments.
    203         * @param w The way to draw.
    204         */
    205         public void visit(Way w) {
    206                 if (w.incomplete || w.nodes.size() < 2)
    207                         return;
    208 
    209                 // show direction arrows, if draw.segment.relevant_directions_only is not set, the way is tagged with a direction key
    210                 // (even if the tag is negated as in oneway=false) or the way is selected
    211 
    212                 boolean showThisDirectionArrow = w.selected
    213                 || (showDirectionArrow && (!showRelevantDirectionsOnly || w.hasDirectionKeys));
    214                 Color wayColor;
    215 
    216                 if (inactive) {
    217                         wayColor = inactiveColor;
    218                 } else if (!w.tagged) {
    219                         wayColor = untaggedWayColor;
    220                 } else {
    221                         wayColor = dfltWayColor;
    222                 }
    223 
    224                 Iterator<Node> it = w.nodes.iterator();
    225                 if (it.hasNext()) {
    226                         Point lastP = nc.getPoint(it.next().eastNorth);
    227                         for (int orderNumber = 1; it.hasNext(); orderNumber++) {
    228                                 Point p = nc.getPoint(it.next().eastNorth);
    229                                 drawSegment(lastP, p, w.selected && !inactive ? selectedColor : wayColor, showThisDirectionArrow);
    230                                 if (showOrderNumber)
    231                                         drawOrderNumber(lastP, p, orderNumber);
    232                                 lastP = p;
    233                         }
    234                 }
    235         }
    236 
    237         private Stroke relatedWayStroke = new BasicStroke(
    238                 4, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_BEVEL);
    239         public void visit(Relation r) {
    240                 if (r.incomplete) return;
    241 
    242                 Color col;
    243                 if (inactive) {
    244                         col = inactiveColor;
    245                 } else if (r.selected) {
    246                         col = selectedColor;
    247                 } else {
    248                         col = relationColor;
    249                 }
    250                 g.setColor(col);
    251 
    252                 for (RelationMember m : r.members) {
    253                         if (m.member.incomplete || m.member.deleted) continue;
    254 
    255                         if (m.member instanceof Node) {
    256                                 Point p = nc.getPoint(((Node) m.member).eastNorth);
    257                                 if (p.x < 0 || p.y < 0
    258                                         || p.x > nc.getWidth() || p.y > nc.getHeight()) continue;
    259 
    260                                 g.drawOval(p.x-3, p.y-3, 6, 6);
    261                         } else if (m.member instanceof Way) {
    262                                 GeneralPath path = new GeneralPath();
    263 
    264                                 boolean first = true;
    265                                 for (Node n : ((Way) m.member).nodes) {
    266                                         if (n.incomplete || n.deleted) continue;
    267                                         Point p = nc.getPoint(n.eastNorth);
    268                                         if (first) {
    269                                                 path.moveTo(p.x, p.y);
    270                                                 first = false;
    271                                         } else {
    272                                                 path.lineTo(p.x, p.y);
    273                                         }
    274                                 }
    275 
    276                                 ((Graphics2D) g).draw(relatedWayStroke.createStrokedShape(path));
    277                         }
    278                 }
    279         }
    280 
    281         /**
    282         * Draw an number of the order of the two consecutive nodes within the
    283         * parents way
    284         */
    285         protected void drawOrderNumber(Point p1, Point p2, int orderNumber) {
    286                 if (isSegmentVisible(p1, p2) && isLargeSegment(p1, p2, segmentNumberSpace)) {
    287                         String on = Integer.toString(orderNumber);
    288                         int strlen = on.length();
    289                         int x = (p1.x+p2.x)/2 - 4*strlen;
    290                         int y = (p1.y+p2.y)/2 + 4;
    291 
    292                         if(virtualNodeSize != 0 && isLargeSegment(p1, p2, virtualNodeSpace))
    293                         {
    294                                 y = (p1.y+p2.y)/2 - virtualNodeSize - 3;
    295                         }
    296 
    297                         displaySegments(); // draw nodes on top!
    298                         Color c = g.getColor();
    299                         g.setColor(backgroundColor);
    300                         g.fillRect(x-1, y-12, 8*strlen+1, 14);
    301                         g.setColor(c);
    302                         g.drawString(on, x, y);
    303                 }
    304         }
    305 
    306         /**
    307         * Draw the node as small rectangle with the given color.
    308         *
    309          * @param n             The node to draw.
    310         * @param color The color of the node.
    311         */
    312         public void drawNode(Node n, Color color, int size, int radius, boolean fill) {
    313                 if (size > 1) {
    314                         Point p = nc.getPoint(n.eastNorth);
    315                         if ((p.x < 0) || (p.y < 0) || (p.x > nc.getWidth())
    316                                         || (p.y > nc.getHeight()))
    317                                 return;
    318                         g.setColor(color);
    319                         if (fill) {
    320                                 g.fillRect(p.x - radius, p.y - radius, size, size);
    321                                 g.drawRect(p.x - radius, p.y - radius, size, size);
    322                         } else
    323                                 g.drawRect(p.x - radius, p.y - radius, size, size);
    324                 }
    325         }
    326 
    327         /**
    328         * Draw a line with the given color.
    329         */
    330         protected void drawSegment(Point p1, Point p2, Color col, boolean showDirection) {
    331                 if (col != currentColor) displaySegments(col);
    332 
    333                 if (isSegmentVisible(p1, p2)) {
    334                         currentPath.moveTo(p1.x, p1.y);
    335                         currentPath.lineTo(p2.x, p2.y);
    336 
    337                         if (showDirection) {
    338                                 double t = Math.atan2(p2.y-p1.y, p2.x-p1.x) + Math.PI;
    339                                 currentPath.lineTo((int)(p2.x + 10*Math.cos(t-PHI)), (int)(p2.y + 10*Math.sin(t-PHI)));
    340                                 currentPath.moveTo((int)(p2.x + 10*Math.cos(t+PHI)), (int)(p2.y + 10*Math.sin(t+PHI)));
    341                                 currentPath.lineTo(p2.x, p2.y);
    342                         }
    343                 }
    344         }
    345 
    346         protected boolean isSegmentVisible(Point p1, Point p2) {
    347                 if ((p1.x < 0) && (p2.x < 0)) return false;
    348                 if ((p1.y < 0) && (p2.y < 0)) return false;
    349                 if ((p1.x > nc.getWidth()) && (p2.x > nc.getWidth())) return false;
    350                 if ((p1.y > nc.getHeight()) && (p2.y > nc.getHeight())) return false;
    351                 return true;
    352         }
    353 
    354         public void setGraphics(Graphics g) {
    355                 this.g = g;
    356         }
    357 
    358         public void setNavigatableComponent(NavigatableComponent nc) {
    359                 this.nc = nc;
    360         }
    361 
    362         protected void displaySegments() {
    363                 displaySegments(null);
    364         }
    365         protected void displaySegments(Color newColor) {
    366                 if (currentPath != null) {
    367                         g.setColor(currentColor);
    368                         ((Graphics2D) g).draw(currentPath);
    369                         currentPath = new GeneralPath();
    370                         currentColor = newColor;
    371                 }
    372         }
     34    public final static Color darkerblue = new Color(0,0,96);
     35    public final static Color darkblue = new Color(0,0,128);
     36    public final static Color darkgreen = new Color(0,128,0);
     37    public final static Color teal = new Color(0,128,128);
     38
     39    /**
     40    * The environment to paint to.
     41    */
     42    protected Graphics g;
     43    /**
     44    * MapView to get screen coordinates.
     45    */
     46    protected NavigatableComponent nc;
     47
     48    public boolean inactive;
     49
     50    protected static final double PHI = Math.toRadians(20);
     51
     52    /**
     53    * Preferences
     54    */
     55    protected Color inactiveColor;
     56    protected Color selectedColor;
     57    protected Color nodeColor;
     58    protected Color dfltWayColor;
     59    protected Color relationColor;
     60    protected Color untaggedWayColor;
     61    protected Color incompleteColor;
     62    protected Color backgroundColor;
     63    protected boolean showDirectionArrow;
     64    protected boolean showRelevantDirectionsOnly;
     65    protected boolean showOrderNumber;
     66    protected boolean fillSelectedNode;
     67    protected boolean fillUnselectedNode;
     68    protected int selectedNodeRadius;
     69    protected int unselectedNodeRadius;
     70    protected int selectedNodeSize;
     71    protected int unselectedNodeSize;
     72    protected int defaultSegmentWidth;
     73    protected int virtualNodeSize;
     74    protected int virtualNodeSpace;
     75    protected int segmentNumberSpace;
     76    protected int taggedNodeRadius;
     77    protected int taggedNodeSize;
     78
     79    /**
     80    * Draw subsequent segments of same color as one Path
     81    */
     82    protected Color currentColor = null;
     83    protected GeneralPath currentPath = new GeneralPath();
     84
     85    Rectangle bbox = new Rectangle();
     86
     87    protected void getSettings(Boolean virtual) {
     88        inactiveColor = Main.pref.getColor(marktr("inactive"), Color.DARK_GRAY);
     89        selectedColor = Main.pref.getColor(marktr("selected"), Color.WHITE);
     90        nodeColor = Main.pref.getColor(marktr("node"), Color.RED);
     91        dfltWayColor = Main.pref.getColor(marktr("way"), darkblue);
     92        relationColor = Main.pref.getColor(marktr("relation"), teal);
     93        untaggedWayColor = Main.pref.getColor(marktr("untagged way"), darkgreen);
     94        incompleteColor = Main.pref.getColor(marktr("incomplete way"), darkerblue);
     95        backgroundColor = Main.pref.getColor(marktr("background"), Color.BLACK);
     96        showDirectionArrow = Main.pref.getBoolean("draw.segment.direction");
     97        showRelevantDirectionsOnly = Main.pref.getBoolean("draw.segment.relevant_directions_only");
     98        showOrderNumber = Main.pref.getBoolean("draw.segment.order_number");
     99        selectedNodeRadius = Main.pref.getInteger("mappaint.node.selected-size", 5) / 2;
     100        selectedNodeSize = selectedNodeRadius * 2;
     101        unselectedNodeRadius = Main.pref.getInteger("mappaint.node.unselected-size", 3) / 2;
     102        unselectedNodeSize = unselectedNodeRadius * 2;
     103        taggedNodeRadius = Main.pref.getInteger("mappaint.node.tagged-size", 5) / 2;
     104        taggedNodeSize = taggedNodeRadius * 2;
     105        defaultSegmentWidth = Main.pref.getInteger("mappaint.segment.default-width", 2);
     106        fillSelectedNode = Main.pref.getBoolean("mappaint.node.fill-selected", true);
     107        fillUnselectedNode = Main.pref.getBoolean("mappaint.node.fill-unselected", false);
     108        virtualNodeSize = virtual ? Main.pref.getInteger("mappaint.node.virtual-size", 8) / 2 : 0;
     109        virtualNodeSpace = Main.pref.getInteger("mappaint.node.virtual-space", 70);
     110        segmentNumberSpace = Main.pref.getInteger("mappaint.segmentnumber.space", 40);
     111
     112        ((Graphics2D)g).setRenderingHint(RenderingHints.KEY_ANTIALIASING,
     113            Main.pref.getBoolean("mappaint.use-antialiasing", false) ?
     114            RenderingHints.VALUE_ANTIALIAS_ON : RenderingHints.VALUE_ANTIALIAS_OFF);
     115    }
     116
     117    public void visitAll(DataSet data, Boolean virtual) {
     118        getSettings(virtual);
     119        // draw tagged ways first, then untagged ways. takes
     120        // time to iterate through list twice, OTOH does not
     121        // require changing the colour while painting...
     122        for (final OsmPrimitive osm : data.relations)
     123            if (!osm.deleted && !osm.selected)
     124                osm.visit(this);
     125
     126        for (final OsmPrimitive osm : data.ways)
     127            if (!osm.deleted && !osm.selected && osm.tagged)
     128                osm.visit(this);
     129        displaySegments();
     130
     131        for (final OsmPrimitive osm : data.ways)
     132            if (!osm.deleted && !osm.selected && !osm.tagged)
     133                osm.visit(this);
     134        displaySegments();
     135
     136        for (final OsmPrimitive osm : data.getSelected())
     137            if (!osm.deleted)
     138                osm.visit(this);
     139        displaySegments();
     140
     141        for (final OsmPrimitive osm : data.nodes)
     142            if (!osm.deleted && !osm.selected)
     143                osm.visit(this);
     144        if(virtualNodeSize != 0)
     145        {
     146            currentColor = nodeColor;
     147            for (final OsmPrimitive osm : data.ways)
     148                if (!osm.deleted)
     149                    visitVirtual((Way)osm);
     150            displaySegments();
     151        }
     152    }
     153
     154    /**
     155    * Draw a small rectangle.
     156    * White if selected (as always) or red otherwise.
     157    *
     158    * @param n The node to draw.
     159    */
     160    public void visit(Node n) {
     161        if (n.incomplete) return;
     162
     163        if (inactive)
     164            drawNode(n, inactiveColor, unselectedNodeSize, unselectedNodeRadius, fillUnselectedNode);
     165        else if (n.selected)
     166            drawNode(n, selectedColor, selectedNodeSize, selectedNodeRadius, fillSelectedNode);
     167        else if(n.tagged)
     168            drawNode(n, nodeColor, taggedNodeSize, taggedNodeRadius, fillUnselectedNode);
     169        else
     170            drawNode(n, nodeColor, unselectedNodeSize, unselectedNodeRadius, fillUnselectedNode);
     171    }
     172
     173    public static Boolean isLargeSegment(Point p1, Point p2, int space)
     174    {
     175        int xd = p1.x-p2.x; if(xd < 0) xd = -xd;
     176        int yd = p1.y-p2.y; if(yd < 0) yd = -yd;
     177        return (xd+yd > space);
     178    }
     179
     180    public void visitVirtual(Way w) {
     181        Iterator<Node> it = w.nodes.iterator();
     182        if (it.hasNext()) {
     183            Point lastP = nc.getPoint(it.next().eastNorth);
     184            while(it.hasNext())
     185            {
     186                Point p = nc.getPoint(it.next().eastNorth);
     187                if(isSegmentVisible(lastP, p) && isLargeSegment(lastP, p, virtualNodeSpace))
     188                {
     189                    int x = (p.x+lastP.x)/2;
     190                    int y = (p.y+lastP.y)/2;
     191                    currentPath.moveTo(x-virtualNodeSize, y);
     192                    currentPath.lineTo(x+virtualNodeSize, y);
     193                    currentPath.moveTo(x, y-virtualNodeSize);
     194                    currentPath.lineTo(x, y+virtualNodeSize);
     195                }
     196                lastP = p;
     197            }
     198        }
     199    }
     200
     201    /**
     202    * Draw a darkblue line for all segments.
     203    * @param w The way to draw.
     204    */
     205    public void visit(Way w) {
     206        if (w.incomplete || w.nodes.size() < 2)
     207            return;
     208
     209        // show direction arrows, if draw.segment.relevant_directions_only is not set, the way is tagged with a direction key
     210        // (even if the tag is negated as in oneway=false) or the way is selected
     211
     212        boolean showThisDirectionArrow = w.selected
     213        || (showDirectionArrow && (!showRelevantDirectionsOnly || w.hasDirectionKeys));
     214        Color wayColor;
     215
     216        if (inactive) {
     217            wayColor = inactiveColor;
     218        } else if (!w.tagged) {
     219            wayColor = untaggedWayColor;
     220        } else {
     221            wayColor = dfltWayColor;
     222        }
     223
     224        Iterator<Node> it = w.nodes.iterator();
     225        if (it.hasNext()) {
     226            Point lastP = nc.getPoint(it.next().eastNorth);
     227            for (int orderNumber = 1; it.hasNext(); orderNumber++) {
     228                Point p = nc.getPoint(it.next().eastNorth);
     229                drawSegment(lastP, p, w.selected && !inactive ? selectedColor : wayColor, showThisDirectionArrow);
     230                if (showOrderNumber)
     231                    drawOrderNumber(lastP, p, orderNumber);
     232                lastP = p;
     233            }
     234        }
     235    }
     236
     237    private Stroke relatedWayStroke = new BasicStroke(
     238        4, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_BEVEL);
     239    public void visit(Relation r) {
     240        if (r.incomplete) return;
     241
     242        Color col;
     243        if (inactive) {
     244            col = inactiveColor;
     245        } else if (r.selected) {
     246            col = selectedColor;
     247        } else {
     248            col = relationColor;
     249        }
     250        g.setColor(col);
     251
     252        for (RelationMember m : r.members) {
     253            if (m.member.incomplete || m.member.deleted) continue;
     254
     255            if (m.member instanceof Node) {
     256                Point p = nc.getPoint(((Node) m.member).eastNorth);
     257                if (p.x < 0 || p.y < 0
     258                    || p.x > nc.getWidth() || p.y > nc.getHeight()) continue;
     259
     260                g.drawOval(p.x-3, p.y-3, 6, 6);
     261            } else if (m.member instanceof Way) {
     262                GeneralPath path = new GeneralPath();
     263
     264                boolean first = true;
     265                for (Node n : ((Way) m.member).nodes) {
     266                    if (n.incomplete || n.deleted) continue;
     267                    Point p = nc.getPoint(n.eastNorth);
     268                    if (first) {
     269                        path.moveTo(p.x, p.y);
     270                        first = false;
     271                    } else {
     272                        path.lineTo(p.x, p.y);
     273                    }
     274                }
     275
     276                ((Graphics2D) g).draw(relatedWayStroke.createStrokedShape(path));
     277            }
     278        }
     279    }
     280
     281    /**
     282    * Draw an number of the order of the two consecutive nodes within the
     283    * parents way
     284    */
     285    protected void drawOrderNumber(Point p1, Point p2, int orderNumber) {
     286        if (isSegmentVisible(p1, p2) && isLargeSegment(p1, p2, segmentNumberSpace)) {
     287            String on = Integer.toString(orderNumber);
     288            int strlen = on.length();
     289            int x = (p1.x+p2.x)/2 - 4*strlen;
     290            int y = (p1.y+p2.y)/2 + 4;
     291
     292            if(virtualNodeSize != 0 && isLargeSegment(p1, p2, virtualNodeSpace))
     293            {
     294                y = (p1.y+p2.y)/2 - virtualNodeSize - 3;
     295            }
     296
     297            displaySegments(); // draw nodes on top!
     298            Color c = g.getColor();
     299            g.setColor(backgroundColor);
     300            g.fillRect(x-1, y-12, 8*strlen+1, 14);
     301            g.setColor(c);
     302            g.drawString(on, x, y);
     303        }
     304    }
     305
     306    /**
     307    * Draw the node as small rectangle with the given color.
     308    *
     309     * @param n     The node to draw.
     310    * @param color The color of the node.
     311    */
     312    public void drawNode(Node n, Color color, int size, int radius, boolean fill) {
     313        if (size > 1) {
     314            Point p = nc.getPoint(n.eastNorth);
     315            if ((p.x < 0) || (p.y < 0) || (p.x > nc.getWidth())
     316                    || (p.y > nc.getHeight()))
     317                return;
     318            g.setColor(color);
     319            if (fill) {
     320                g.fillRect(p.x - radius, p.y - radius, size, size);
     321                g.drawRect(p.x - radius, p.y - radius, size, size);
     322            } else
     323                g.drawRect(p.x - radius, p.y - radius, size, size);
     324        }
     325    }
     326
     327    /**
     328    * Draw a line with the given color.
     329    */
     330    protected void drawSegment(Point p1, Point p2, Color col, boolean showDirection) {
     331        if (col != currentColor) displaySegments(col);
     332
     333        if (isSegmentVisible(p1, p2)) {
     334            currentPath.moveTo(p1.x, p1.y);
     335            currentPath.lineTo(p2.x, p2.y);
     336
     337            if (showDirection) {
     338                double t = Math.atan2(p2.y-p1.y, p2.x-p1.x) + Math.PI;
     339                currentPath.lineTo((int)(p2.x + 10*Math.cos(t-PHI)), (int)(p2.y + 10*Math.sin(t-PHI)));
     340                currentPath.moveTo((int)(p2.x + 10*Math.cos(t+PHI)), (int)(p2.y + 10*Math.sin(t+PHI)));
     341                currentPath.lineTo(p2.x, p2.y);
     342            }
     343        }
     344    }
     345
     346    protected boolean isSegmentVisible(Point p1, Point p2) {
     347        if ((p1.x < 0) && (p2.x < 0)) return false;
     348        if ((p1.y < 0) && (p2.y < 0)) return false;
     349        if ((p1.x > nc.getWidth()) && (p2.x > nc.getWidth())) return false;
     350        if ((p1.y > nc.getHeight()) && (p2.y > nc.getHeight())) return false;
     351        return true;
     352    }
     353
     354    public void setGraphics(Graphics g) {
     355        this.g = g;
     356    }
     357
     358    public void setNavigatableComponent(NavigatableComponent nc) {
     359        this.nc = nc;
     360    }
     361
     362    protected void displaySegments() {
     363        displaySegments(null);
     364    }
     365    protected void displaySegments(Color newColor) {
     366        if (currentPath != null) {
     367            g.setColor(currentColor);
     368            ((Graphics2D) g).draw(currentPath);
     369            currentPath = new GeneralPath();
     370            currentColor = newColor;
     371        }
     372    }
    373373}
  • trunk/src/org/openstreetmap/josm/data/osm/visitor/Visitor.java

    r627 r1169  
    99 * Implementation of the visitor scheme. Every OsmPrimitive can be visited by
    1010 * several different visitors.
    11  * 
     11 *
    1212 * @author imi
    1313 */
    1414public interface Visitor {
    15         void visit(Node n);
    16         void visit(Way w);
    17         void visit(Relation e);
     15    void visit(Node n);
     16    void visit(Way w);
     17    void visit(Relation e);
    1818}
  • trunk/src/org/openstreetmap/josm/data/projection/Epsg4326.java

    r1032 r1169  
    1212public class Epsg4326 implements Projection {
    1313
    14         public EastNorth latlon2eastNorth(LatLon p) {
    15                 return new EastNorth(p.lon(), p.lat());
    16         }
     14    public EastNorth latlon2eastNorth(LatLon p) {
     15        return new EastNorth(p.lon(), p.lat());
     16    }
    1717
    18         public LatLon eastNorth2latlon(EastNorth p) {
    19                 return new LatLon(p.north(), p.east());
    20         }
     18    public LatLon eastNorth2latlon(EastNorth p) {
     19        return new LatLon(p.north(), p.east());
     20    }
    2121
    22         @Override public String toString() {
    23                 return tr("EPSG:4326");
    24         }
     22    @Override public String toString() {
     23        return tr("EPSG:4326");
     24    }
    2525
    2626    public String getCacheDirectoryName() {
     
    2828    }
    2929
    30         public double scaleFactor() {
    31             return 1.0/360;
     30    public double scaleFactor() {
     31        return 1.0/360;
    3232    }
    3333
    34         @Override public boolean equals(Object o) {
    35                 return o instanceof Epsg4326;
    36         }
     34    @Override public boolean equals(Object o) {
     35        return o instanceof Epsg4326;
     36    }
    3737
    38         @Override public int hashCode() {
    39                 return Epsg4326.class.hashCode();
    40         }
     38    @Override public int hashCode() {
     39        return Epsg4326.class.hashCode();
     40    }
    4141}
  • trunk/src/org/openstreetmap/josm/data/projection/Lambert.java

    r865 r1169  
    1414
    1515public class Lambert implements Projection {
    16         /**
    17         * Lambert I, II, III, and IV projection exponents
    18         */
    19         public static final double n[] = { 0.7604059656, 0.7289686274, 0.6959127966, 0.6712679322 };
    20 
    21         /**
    22         * Lambert I, II, III, and IV projection constants
    23         */
    24         public static final double c[] = { 11603796.98, 11745793.39, 11947992.52, 12136281.99 };
    25 
    26         /**
    27         * Lambert I, II, III, and IV false east
    28         */
    29         public static final double Xs[] = { 600000.0, 600000.0, 600000.0, 234.358 };
    30 
    31         /**
    32         * Lambert I, II, III, and IV false north
    33         */
    34         public static final double Ys[] = { 5657616.674, 6199695.768, 6791905.085, 7239161.542 };
    35 
    36         /**
    37         * Lambert I, II, III, and IV longitudinal offset to Greenwich meridian
    38         */
    39         public static final double lg0 = 0.04079234433198; // 2deg20'14.025"
    40 
    41         /**
    42         * precision in iterative schema
    43         */
    44 
    45         public static final double epsilon = 1e-11;
    46 
    47         /**
    48         * France is divided in 4 Lambert projection zones (1,2,3 + 4th for Corsica)
    49         */
    50         public static final double cMaxLatZone1 = Math.toRadians(57 * 0.9);
    51 
    52         public static final double zoneLimits[] = { Math.toRadians(53.5 * 0.9), // between Zone 1 and Zone 2 (in grad *0.9)
    53                         Math.toRadians(50.5 * 0.9), // between Zone 2 and Zone 3
    54                         Math.toRadians(47.51963 * 0.9), // between Zone 3 and Zone 4
    55                         Math.toRadians(46.17821 * 0.9) };// lowest latitude of Zone 4
    56 
    57         public static final double cMinLonZones = Math.toRadians(-4.9074074074074059 * 0.9);
    58 
    59         public static final double cMaxLonZones = Math.toRadians(10.2 * 0.9);
    60 
    61         /**
    62         *  Because josm cannot work correctly if two zones are displayed, we allow some overlapping
    63         */
    64         public static final double cMaxOverlappingZones = Math.toRadians(1.5 * 0.9);
    65 
    66         public static int layoutZone = -1;
    67 
    68         private static int currentZone = 0;
    69 
    70         private static boolean dontDisplayErrors = false;
    71 
    72         /**
    73         * @param p  WGS84 lat/lon (ellipsoid GRS80) (in degree)
    74         * @return eastnorth projection in Lambert Zone (ellipsoid Clark)
    75         */
    76         public EastNorth latlon2eastNorth(LatLon p) {
    77                 // translate ellipsoid GRS80 (WGS83) => Clark
    78                 LatLon geo = GRS802Clark(p);
    79                 double lt = geo.lat(); // in radian
    80                 double lg = geo.lon();
    81 
    82                 // check if longitude and latitude are inside the french Lambert zones
    83                 currentZone = 0;
    84                 boolean outOfLambertZones = false;
    85                 if (lt >= zoneLimits[3] && lt <= cMaxLatZone1 && lg >= cMinLonZones && lg <= cMaxLonZones) {
    86                         // zone I
    87                         if (lt > zoneLimits[0])
    88                                 currentZone = 0;
    89                         // zone II
    90                         else if (lt > zoneLimits[1])
    91                                 currentZone = 1;
    92                         // zone III
    93                         else if (lt > zoneLimits[2])
    94                                 currentZone = 2;
    95                         // zone III or IV
    96                         else if (lt > zoneLimits[3])
    97                                 // Note: zone IV is dedicated to Corsica island and extends from 47.8 to
    98                                 // 45.9 degrees of latitude. There is an overlap with zone III that can be
    99                                 // solved only with longitude (covers Corsica if lon > 7.2 degree)
    100                                 if (lg < Math.toRadians(8 * 0.9))
    101                                         currentZone = 2;
    102                                 else
    103                                         currentZone = 3;
    104                 } else {
    105                         outOfLambertZones = true; // possible when MAX_LAT is used
    106                         if (p.lat() != 0 && Math.abs(p.lat()) != Projection.MAX_LAT
    107                                         && p.lon() != 0 && Math.abs(p.lon()) != Projection.MAX_LON
    108                                         && dontDisplayErrors == false) {
    109                                 JOptionPane.showMessageDialog(Main.parent,
    110                                                 tr("The projection \"{0}\" is designed for\n"
    111                                                 + "latitudes between 46.1° and 57° only.\n"
    112                                                 + "Use another projection system if you are not using\n"
    113                                                 + "a french WMS server.\n"
    114                                                 + "Do not upload any data after this message.", this.toString()));
    115                                 dontDisplayErrors = true;
    116                         }
    117                 }
    118                 if (!outOfLambertZones) {
    119                         if (layoutZone == -1) {
    120                                 layoutZone = currentZone;
    121                                 dontDisplayErrors = false;
    122                         } else if (layoutZone != currentZone) {
    123                                 if ((currentZone < layoutZone && Math.abs(zoneLimits[currentZone] - lt) > cMaxOverlappingZones)
    124                                                 || (currentZone > layoutZone && Math.abs(zoneLimits[layoutZone] - lt) > cMaxOverlappingZones)) {
    125                                         JOptionPane.showMessageDialog(Main.parent,
    126                                                                         tr("IMPORTANT : data positionned far away from\n"
    127                                                                                         + "the current Lambert zone limits.\n"
    128                                                                                         + "Do not upload any data after this message.\n"
    129                                                                                         + "Undo your last action, Save your work \n"
    130                                                                                         + "and Start a new layer on the new zone."));
    131                                         layoutZone = -1;
    132                                         dontDisplayErrors = true;
    133                                 } else {
    134                                         System.out.println("temporarily extend Lambert zone " + layoutZone + " projection at lat,lon:"
    135                                                         + lt + "," + lg);
    136                                 }
    137                         }
    138                 }
    139                 if (layoutZone == -1) {
    140                         return ConicProjection(lt, lg, Xs[currentZone], Ys[currentZone], c[currentZone], n[currentZone]);
    141                 } // else
    142                 return ConicProjection(lt, lg, Xs[layoutZone], Ys[layoutZone], c[layoutZone], n[layoutZone]);
    143         }
    144 
    145         public LatLon eastNorth2latlon(EastNorth p) {
    146                 LatLon geo;
    147                 if (layoutZone == -1)
    148                         // possible until the Lambert zone is determined by latlon2eastNorth() with a valid LatLon
    149                         geo = Geographic(p, Xs[currentZone], Ys[currentZone], c[currentZone], n[currentZone]);
    150                 else
    151                         geo = Geographic(p, Xs[layoutZone], Ys[layoutZone], c[layoutZone], n[layoutZone]);
    152                 // translate ellipsoid Clark => GRS80 (WGS83)
    153                 LatLon wgs = Clark2GRS80(geo);
    154                 return new LatLon(Math.toDegrees(wgs.lat()), Math.toDegrees(wgs.lon()));
    155         }
    156 
    157         @Override public String toString() {
    158                 return tr("Lambert Zone (France)");
    159         }
    160 
    161         public String getCacheDirectoryName() {
    162                 return "lambert";
    163         }
    164 
    165         public double scaleFactor() {
    166                 return 1.0 / 360;
    167         }
    168 
    169         @Override
    170         public boolean equals(Object o) {
    171                 return o instanceof Lambert;
    172         }
    173 
    174         @Override
    175         public int hashCode() {
    176                 return Lambert.class.hashCode();
    177         }
    178 
    179         /**
    180         * Initializes from geographic coordinates. Note that reference ellipsoid
    181         * used by Lambert is the Clark ellipsoid.
    182         *
    183         * @param lat latitude in grad
    184         * @param lon longitude in grad
    185         * @param Xs  false east (coordinate system origin) in meters
    186         * @param Ys  false north (coordinate system origin) in meters
    187         * @param c   projection constant
    188         * @param n   projection exponent
    189         * @return EastNorth projected coordinates in meter
    190         */
    191         private EastNorth ConicProjection(double lat, double lon, double Xs, double Ys, double c, double n) {
    192                 double eslt = Ellipsoid.clarke.e * Math.sin(lat);
    193                 double l = Math.log(Math.tan(Math.PI / 4.0 + (lat / 2.0))
    194                                 * Math.pow((1.0 - eslt) / (1.0 + eslt), Ellipsoid.clarke.e / 2.0));
    195                 double east = Xs + c * Math.exp(-n * l) * Math.sin(n * (lon - lg0));
    196                 double north = Ys - c * Math.exp(-n * l) * Math.cos(n * (lon - lg0));
    197                 return new EastNorth(east, north);
    198         }
    199 
    200         /**
    201         * Initializes from projected coordinates (conic projection). Note that
    202         * reference ellipsoid used by Lambert is Clark
    203         *
    204         * @param coord projected coordinates pair in meters
    205         * @param Xs    false east (coordinate system origin) in meters
    206         * @param Ys    false north (coordinate system origin) in meters
    207         * @param c     projection constant
    208         * @param n     projection exponent
    209         * @return LatLon in radian
    210         */
    211         private LatLon Geographic(EastNorth eastNorth, double Xs, double Ys, double c, double n) {
    212                 double dx = eastNorth.east() - Xs;
    213                 double dy = Ys - eastNorth.north();
    214                 double R = Math.sqrt(dx * dx + dy * dy);
    215                 double gamma = Math.atan(dx / dy);
    216                 double l = -1.0 / n * Math.log(Math.abs(R / c));
    217                 l = Math.exp(l);
    218                 double lon = lg0 + gamma / n;
    219                 double lat = 2.0 * Math.atan(l) - Math.PI / 2.0;
    220                 double delta = 1.0;
    221                 while (delta > epsilon) {
    222                         double eslt = Ellipsoid.clarke.e * Math.sin(lat);
    223                         double nlt = 2.0 * Math.atan(Math.pow((1.0 + eslt) / (1.0 - eslt), Ellipsoid.clarke.e / 2.0) * l) - Math.PI
    224                                         / 2.0;
    225                         delta = Math.abs(nlt - lat);
    226                         lat = nlt;
    227                 }
    228                 return new LatLon(lat, lon); // in radian
    229         }
    230 
    231         /**
    232         * Translate latitude/longitude in WGS84, (ellipsoid GRS80) to Lambert
    233         * geographic, (ellipsoid Clark)
    234         *
    235         * @param wgs
    236         * @return
    237         */
    238         private LatLon GRS802Clark(LatLon wgs) {
    239                 double lat = Math.toRadians(wgs.lat()); // degree to radian
    240                 double lon = Math.toRadians(wgs.lon());
    241                 // WGS84 geographic => WGS84 cartesian
    242                 double N = Ellipsoid.GRS80.a / (Math.sqrt(1.0 - Ellipsoid.GRS80.e2 * Math.sin(lat) * Math.sin(lat)));
    243                 double X = (N/* +height */) * Math.cos(lat) * Math.cos(lon);
    244                 double Y = (N/* +height */) * Math.cos(lat) * Math.sin(lon);
    245                 double Z = (N * (1.0 - Ellipsoid.GRS80.e2)/* + height */) * Math.sin(lat);
    246                 // WGS84 => Lambert ellipsoide similarity
    247                 X += 168.0;
    248                 Y += 60.0;
    249                 Z += -320.0;
    250                 // Lambert cartesian => Lambert geographic
    251                 return Geographic(X, Y, Z, Ellipsoid.clarke);
    252         }
    253 
    254         /**
    255         * @param lambert
    256         * @return
    257         */
    258         private LatLon Clark2GRS80(LatLon lambert) {
    259                 double lat = lambert.lat(); // in radian
    260                 double lon = lambert.lon();
    261                 // Lambert geographic => Lambert cartesian
    262                 double N = Ellipsoid.clarke.a / (Math.sqrt(1.0 - Ellipsoid.clarke.e2 * Math.sin(lat) * Math.sin(lat)));
    263                 double X = (N/* +height */) * Math.cos(lat) * Math.cos(lon);
    264                 double Y = (N/* +height */) * Math.cos(lat) * Math.sin(lon);
    265                 double Z = (N * (1.0 - Ellipsoid.clarke.e2)/* + height */) * Math.sin(lat);
    266                 // Lambert => WGS84 ellipsoide similarity
    267                 X += -168.0;
    268                 Y += -60.0;
    269                 Z += 320.0;
    270                 // WGS84 cartesian => WGS84 geographic
    271                 return Geographic(X, Y, Z, Ellipsoid.GRS80);
    272         }
    273 
    274         /**
    275         * initializes from cartesian coordinates
    276         *
    277         * @param X
    278         *            1st coordinate in meters
    279         * @param Y
    280         *            2nd coordinate in meters
    281         * @param Z
    282         *            3rd coordinate in meters
    283         * @param ell
    284         *            reference ellipsoid
    285         */
    286         private LatLon Geographic(double X, double Y, double Z, Ellipsoid ell) {
    287                 double norm = Math.sqrt(X * X + Y * Y);
    288                 double lg = 2.0 * Math.atan(Y / (X + norm));
    289                 double lt = Math.atan(Z / (norm * (1.0 - (ell.a * ell.e2 / Math.sqrt(X * X + Y * Y + Z * Z)))));
    290                 double delta = 1.0;
    291                 while (delta > epsilon) {
    292                         double s2 = Math.sin(lt);
    293                         s2 *= s2;
    294                         double l = Math.atan((Z / norm)
    295                                 / (1.0 - (ell.a * ell.e2 * Math.cos(lt) / (norm * Math.sqrt(1.0 - ell.e2 * s2)))));
    296                         delta = Math.abs(l - lt);
    297                         lt = l;
    298                 }
    299                 double s2 = Math.sin(lt);
    300                 s2 *= s2;
    301                 // h = norm / Math.cos(lt) - ell.a / Math.sqrt(1.0 - ell.e2 * s2);
    302                 return new LatLon(lt, lg);
    303         }
     16    /**
     17    * Lambert I, II, III, and IV projection exponents
     18    */
     19    public static final double n[] = { 0.7604059656, 0.7289686274, 0.6959127966, 0.6712679322 };
     20
     21    /**
     22    * Lambert I, II, III, and IV projection constants
     23    */
     24    public static final double c[] = { 11603796.98, 11745793.39, 11947992.52, 12136281.99 };
     25
     26    /**
     27    * Lambert I, II, III, and IV false east
     28    */
     29    public static final double Xs[] = { 600000.0, 600000.0, 600000.0, 234.358 };
     30
     31    /**
     32    * Lambert I, II, III, and IV false north
     33    */
     34    public static final double Ys[] = { 5657616.674, 6199695.768, 6791905.085, 7239161.542 };
     35
     36    /**
     37    * Lambert I, II, III, and IV longitudinal offset to Greenwich meridian
     38    */
     39    public static final double lg0 = 0.04079234433198; // 2deg20'14.025"
     40
     41    /**
     42    * precision in iterative schema
     43    */
     44
     45    public static final double epsilon = 1e-11;
     46
     47    /**
     48    * France is divided in 4 Lambert projection zones (1,2,3 + 4th for Corsica)
     49    */
     50    public static final double cMaxLatZone1 = Math.toRadians(57 * 0.9);
     51
     52    public static final double zoneLimits[] = { Math.toRadians(53.5 * 0.9), // between Zone 1 and Zone 2 (in grad *0.9)
     53            Math.toRadians(50.5 * 0.9), // between Zone 2 and Zone 3
     54            Math.toRadians(47.51963 * 0.9), // between Zone 3 and Zone 4
     55            Math.toRadians(46.17821 * 0.9) };// lowest latitude of Zone 4
     56
     57    public static final double cMinLonZones = Math.toRadians(-4.9074074074074059 * 0.9);
     58
     59    public static final double cMaxLonZones = Math.toRadians(10.2 * 0.9);
     60
     61    /**
     62    *  Because josm cannot work correctly if two zones are displayed, we allow some overlapping
     63    */
     64    public static final double cMaxOverlappingZones = Math.toRadians(1.5 * 0.9);
     65
     66    public static int layoutZone = -1;
     67
     68    private static int currentZone = 0;
     69
     70    private static boolean dontDisplayErrors = false;
     71
     72    /**
     73    * @param p  WGS84 lat/lon (ellipsoid GRS80) (in degree)
     74    * @return eastnorth projection in Lambert Zone (ellipsoid Clark)
     75    */
     76    public EastNorth latlon2eastNorth(LatLon p) {
     77        // translate ellipsoid GRS80 (WGS83) => Clark
     78        LatLon geo = GRS802Clark(p);
     79        double lt = geo.lat(); // in radian
     80        double lg = geo.lon();
     81
     82        // check if longitude and latitude are inside the french Lambert zones
     83        currentZone = 0;
     84        boolean outOfLambertZones = false;
     85        if (lt >= zoneLimits[3] && lt <= cMaxLatZone1 && lg >= cMinLonZones && lg <= cMaxLonZones) {
     86            // zone I
     87            if (lt > zoneLimits[0])
     88                currentZone = 0;
     89            // zone II
     90            else if (lt > zoneLimits[1])
     91                currentZone = 1;
     92            // zone III
     93            else if (lt > zoneLimits[2])
     94                currentZone = 2;
     95            // zone III or IV
     96            else if (lt > zoneLimits[3])
     97                // Note: zone IV is dedicated to Corsica island and extends from 47.8 to
     98                // 45.9 degrees of latitude. There is an overlap with zone III that can be
     99                // solved only with longitude (covers Corsica if lon > 7.2 degree)
     100                if (lg < Math.toRadians(8 * 0.9))
     101                    currentZone = 2;
     102                else
     103                    currentZone = 3;
     104        } else {
     105            outOfLambertZones = true; // possible when MAX_LAT is used
     106            if (p.lat() != 0 && Math.abs(p.lat()) != Projection.MAX_LAT
     107                    && p.lon() != 0 && Math.abs(p.lon()) != Projection.MAX_LON
     108                    && dontDisplayErrors == false) {
     109                JOptionPane.showMessageDialog(Main.parent,
     110                        tr("The projection \"{0}\" is designed for\n"
     111                        + "latitudes between 46.1° and 57° only.\n"
     112                        + "Use another projection system if you are not using\n"
     113                        + "a french WMS server.\n"
     114                        + "Do not upload any data after this message.", this.toString()));
     115                dontDisplayErrors = true;
     116            }
     117        }
     118        if (!outOfLambertZones) {
     119            if (layoutZone == -1) {
     120                layoutZone = currentZone;
     121                dontDisplayErrors = false;
     122            } else if (layoutZone != currentZone) {
     123                if ((currentZone < layoutZone && Math.abs(zoneLimits[currentZone] - lt) > cMaxOverlappingZones)
     124                        || (currentZone > layoutZone && Math.abs(zoneLimits[layoutZone] - lt) > cMaxOverlappingZones)) {
     125                    JOptionPane.showMessageDialog(Main.parent,
     126                                    tr("IMPORTANT : data positionned far away from\n"
     127                                            + "the current Lambert zone limits.\n"
     128                                            + "Do not upload any data after this message.\n"
     129                                            + "Undo your last action, Save your work \n"
     130                                            + "and Start a new layer on the new zone."));
     131                    layoutZone = -1;
     132                    dontDisplayErrors = true;
     133                } else {
     134                    System.out.println("temporarily extend Lambert zone " + layoutZone + " projection at lat,lon:"
     135                            + lt + "," + lg);
     136                }
     137            }
     138        }
     139        if (layoutZone == -1) {
     140            return ConicProjection(lt, lg, Xs[currentZone], Ys[currentZone], c[currentZone], n[currentZone]);
     141        } // else
     142        return ConicProjection(lt, lg, Xs[layoutZone], Ys[layoutZone], c[layoutZone], n[layoutZone]);
     143    }
     144
     145    public LatLon eastNorth2latlon(EastNorth p) {
     146        LatLon geo;
     147        if (layoutZone == -1)
     148            // possible until the Lambert zone is determined by latlon2eastNorth() with a valid LatLon
     149            geo = Geographic(p, Xs[currentZone], Ys[currentZone], c[currentZone], n[currentZone]);
     150        else
     151            geo = Geographic(p, Xs[layoutZone], Ys[layoutZone], c[layoutZone], n[layoutZone]);
     152        // translate ellipsoid Clark => GRS80 (WGS83)
     153        LatLon wgs = Clark2GRS80(geo);
     154        return new LatLon(Math.toDegrees(wgs.lat()), Math.toDegrees(wgs.lon()));
     155    }
     156
     157    @Override public String toString() {
     158        return tr("Lambert Zone (France)");
     159    }
     160
     161    public String getCacheDirectoryName() {
     162        return "lambert";
     163    }
     164
     165    public double scaleFactor() {
     166        return 1.0 / 360;
     167    }
     168
     169    @Override
     170    public boolean equals(Object o) {
     171        return o instanceof Lambert;
     172    }
     173
     174    @Override
     175    public int hashCode() {
     176        return Lambert.class.hashCode();
     177    }
     178
     179    /**
     180    * Initializes from geographic coordinates. Note that reference ellipsoid
     181    * used by Lambert is the Clark ellipsoid.
     182    *
     183    * @param lat latitude in grad
     184    * @param lon longitude in grad
     185    * @param Xs  false east (coordinate system origin) in meters
     186    * @param Ys  false north (coordinate system origin) in meters
     187    * @param c   projection constant
     188    * @param n   projection exponent
     189    * @return EastNorth projected coordinates in meter
     190    */
     191    private EastNorth ConicProjection(double lat, double lon, double Xs, double Ys, double c, double n) {
     192        double eslt = Ellipsoid.clarke.e * Math.sin(lat);
     193        double l = Math.log(Math.tan(Math.PI / 4.0 + (lat / 2.0))
     194                * Math.pow((1.0 - eslt) / (1.0 + eslt), Ellipsoid.clarke.e / 2.0));
     195        double east = Xs + c * Math.exp(-n * l) * Math.sin(n * (lon - lg0));
     196        double north = Ys - c * Math.exp(-n * l) * Math.cos(n * (lon - lg0));
     197        return new EastNorth(east, north);
     198    }
     199
     200    /**
     201    * Initializes from projected coordinates (conic projection). Note that
     202    * reference ellipsoid used by Lambert is Clark
     203    *
     204    * @param coord projected coordinates pair in meters
     205    * @param Xs    false east (coordinate system origin) in meters
     206    * @param Ys    false north (coordinate system origin) in meters
     207    * @param c     projection constant
     208    * @param n     projection exponent
     209    * @return LatLon in radian
     210    */
     211    private LatLon Geographic(EastNorth eastNorth, double Xs, double Ys, double c, double n) {
     212        double dx = eastNorth.east() - Xs;
     213        double dy = Ys - eastNorth.north();
     214        double R = Math.sqrt(dx * dx + dy * dy);
     215        double gamma = Math.atan(dx / dy);
     216        double l = -1.0 / n * Math.log(Math.abs(R / c));
     217        l = Math.exp(l);
     218        double lon = lg0 + gamma / n;
     219        double lat = 2.0 * Math.atan(l) - Math.PI / 2.0;
     220        double delta = 1.0;
     221        while (delta > epsilon) {
     222            double eslt = Ellipsoid.clarke.e * Math.sin(lat);
     223            double nlt = 2.0 * Math.atan(Math.pow((1.0 + eslt) / (1.0 - eslt), Ellipsoid.clarke.e / 2.0) * l) - Math.PI
     224                    / 2.0;
     225            delta = Math.abs(nlt - lat);
     226            lat = nlt;
     227        }
     228        return new LatLon(lat, lon); // in radian
     229    }
     230
     231    /**
     232    * Translate latitude/longitude in WGS84, (ellipsoid GRS80) to Lambert
     233    * geographic, (ellipsoid Clark)
     234    *
     235    * @param wgs
     236    * @return
     237    */
     238    private LatLon GRS802Clark(LatLon wgs) {
     239        double lat = Math.toRadians(wgs.lat()); // degree to radian
     240        double lon = Math.toRadians(wgs.lon());
     241        // WGS84 geographic => WGS84 cartesian
     242        double N = Ellipsoid.GRS80.a / (Math.sqrt(1.0 - Ellipsoid.GRS80.e2 * Math.sin(lat) * Math.sin(lat)));
     243        double X = (N/* +height */) * Math.cos(lat) * Math.cos(lon);
     244        double Y = (N/* +height */) * Math.cos(lat) * Math.sin(lon);
     245        double Z = (N * (1.0 - Ellipsoid.GRS80.e2)/* + height */) * Math.sin(lat);
     246        // WGS84 => Lambert ellipsoide similarity
     247        X += 168.0;
     248        Y += 60.0;
     249        Z += -320.0;
     250        // Lambert cartesian => Lambert geographic
     251        return Geographic(X, Y, Z, Ellipsoid.clarke);
     252    }
     253
     254    /**
     255    * @param lambert
     256    * @return
     257    */
     258    private LatLon Clark2GRS80(LatLon lambert) {
     259        double lat = lambert.lat(); // in radian
     260        double lon = lambert.lon();
     261        // Lambert geographic => Lambert cartesian
     262        double N = Ellipsoid.clarke.a / (Math.sqrt(1.0 - Ellipsoid.clarke.e2 * Math.sin(lat) * Math.sin(lat)));
     263        double X = (N/* +height */) * Math.cos(lat) * Math.cos(lon);
     264        double Y = (N/* +height */) * Math.cos(lat) * Math.sin(lon);
     265        double Z = (N * (1.0 - Ellipsoid.clarke.e2)/* + height */) * Math.sin(lat);
     266        // Lambert => WGS84 ellipsoide similarity
     267        X += -168.0;
     268        Y += -60.0;
     269        Z += 320.0;
     270        // WGS84 cartesian => WGS84 geographic
     271        return Geographic(X, Y, Z, Ellipsoid.GRS80);
     272    }
     273
     274    /**
     275    * initializes from cartesian coordinates
     276    *
     277    * @param X
     278    *            1st coordinate in meters
     279    * @param Y
     280    *            2nd coordinate in meters
     281    * @param Z
     282    *            3rd coordinate in meters
     283    * @param ell
     284    *            reference ellipsoid
     285    */
     286    private LatLon Geographic(double X, double Y, double Z, Ellipsoid ell) {
     287        double norm = Math.sqrt(X * X + Y * Y);
     288        double lg = 2.0 * Math.atan(Y / (X + norm));
     289        double lt = Math.atan(Z / (norm * (1.0 - (ell.a * ell.e2 / Math.sqrt(X * X + Y * Y + Z * Z)))));
     290        double delta = 1.0;
     291        while (delta > epsilon) {
     292            double s2 = Math.sin(lt);
     293            s2 *= s2;
     294            double l = Math.atan((Z / norm)
     295                / (1.0 - (ell.a * ell.e2 * Math.cos(lt) / (norm * Math.sqrt(1.0 - ell.e2 * s2)))));
     296            delta = Math.abs(l - lt);
     297            lt = l;
     298        }
     299        double s2 = Math.sin(lt);
     300        s2 *= s2;
     301        // h = norm / Math.cos(lt) - ell.a / Math.sqrt(1.0 - ell.e2 * s2);
     302        return new LatLon(lt, lg);
     303    }
    304304
    305305}
  • trunk/src/org/openstreetmap/josm/data/projection/Mercator.java

    r1032 r1169  
    1010 * Implement Mercator Projection code, coded after documentation
    1111 * from wikipedia.
    12  * 
    13  * The center of the mercator projection is always the 0 grad 
     12 *
     13 * The center of the mercator projection is always the 0 grad
    1414 * coordinate.
    15  * 
     15 *
    1616 * @author imi
    1717 */
    1818public class Mercator implements Projection {
    1919
    20         public EastNorth latlon2eastNorth(LatLon p) {
    21                 return new EastNorth(
    22                         p.lon()*Math.PI/180,
    23                         Math.log(Math.tan(Math.PI/4+p.lat()*Math.PI/360)));
    24         }
     20    public EastNorth latlon2eastNorth(LatLon p) {
     21        return new EastNorth(
     22            p.lon()*Math.PI/180,
     23            Math.log(Math.tan(Math.PI/4+p.lat()*Math.PI/360)));
     24    }
    2525
    26         public LatLon eastNorth2latlon(EastNorth p) {
    27                 return new LatLon(
    28                         Math.atan(Math.sinh(p.north()))*180/Math.PI,
    29                         p.east()*180/Math.PI);
    30         }
     26    public LatLon eastNorth2latlon(EastNorth p) {
     27        return new LatLon(
     28            Math.atan(Math.sinh(p.north()))*180/Math.PI,
     29            p.east()*180/Math.PI);
     30    }
    3131
    32         @Override public String toString() {
    33                 return tr("Mercator");
    34         }
     32    @Override public String toString() {
     33        return tr("Mercator");
     34    }
    3535
    3636    public String getCacheDirectoryName() {
     
    3838    }
    3939
    40         public double scaleFactor() {
    41             return 1/Math.PI/2;
     40    public double scaleFactor() {
     41        return 1/Math.PI/2;
    4242    }
    4343
    44         @Override public boolean equals(Object o) {
    45                 return o instanceof Mercator;
    46         }
     44    @Override public boolean equals(Object o) {
     45        return o instanceof Mercator;
     46    }
    4747
    48         @Override public int hashCode() {
    49                 return Mercator.class.hashCode();
    50         }
     48    @Override public int hashCode() {
     49        return Mercator.class.hashCode();
     50    }
    5151}
  • trunk/src/org/openstreetmap/josm/data/projection/Projection.java

    r656 r1169  
    66
    77/**
    8  * Classes implementing this are able to convert lat/lon values to 
     8 * Classes implementing this are able to convert lat/lon values to
    99 * planar screen coordinates.
    10  * 
     10 *
    1111 * @author imi
    1212 */
    1313public interface Projection {
    1414
    15         /**
    16          * Maximum latitude representable.
    17          */
    18         public static final double MAX_LAT = 85.05112877980659; // Mercator squares the world
    19        
    20         /**
    21          * Maximum longditude representable.
    22          */
    23         public static final double MAX_LON = 180;
     15    /**
     16     * Maximum latitude representable.
     17     */
     18    public static final double MAX_LAT = 85.05112877980659; // Mercator squares the world
    2419
    25         /**
    26          * Minimum difference in location to not be represented as the same position.
    27         */
    28         public static final double MAX_SERVER_PRECISION = 1e12;
     20    /**
     21     * Maximum longditude representable.
     22    */
     23    public static final double MAX_LON = 180;
    2924
    30         /**
    31          * List of all available projections.
    32          */
    33         public static Projection[] allProjections = new Projection[]{
    34                 new Epsg4326(),
    35                 new Mercator(),
    36                 new Lambert()
    37         };
    38        
    39         /**
    40          * Convert from lat/lon to northing/easting.
    41          *
    42          * @param p             The geo point to convert. x/y members of the point are filled.
    43          */
    44         EastNorth latlon2eastNorth(LatLon p);
    45        
    46         /**
    47          * Convert from norting/easting to lat/lon.
    48          *
    49          * @param p             The geo point to convert. lat/lon members of the point are filled.
    50          */
    51         LatLon eastNorth2latlon(EastNorth p);
     25    /**
     26     * Minimum difference in location to not be represented as the same position.
     27     */
     28    public static final double MAX_SERVER_PRECISION = 1e12;
    5229
    53         /**
    54          * Describe the projection converter in one or two words.
    55          */
    56         String toString();
    57    
     30    /**
     31     * List of all available projections.
     32     */
     33    public static Projection[] allProjections = new Projection[]{
     34        new Epsg4326(),
     35        new Mercator(),
     36        new Lambert()
     37    };
     38
     39    /**
     40     * Convert from lat/lon to northing/easting.
     41     *
     42     * @param p     The geo point to convert. x/y members of the point are filled.
     43     */
     44    EastNorth latlon2eastNorth(LatLon p);
     45
     46    /**
     47     * Convert from norting/easting to lat/lon.
     48     *
     49     * @param p     The geo point to convert. lat/lon members of the point are filled.
     50     */
     51    LatLon eastNorth2latlon(EastNorth p);
     52
     53    /**
     54     * Describe the projection converter in one or two words.
     55     */
     56    String toString();
     57
    5858    /**
    5959     * Get a filename compatible string (for the cache directory)
    6060     */
    6161    String getCacheDirectoryName();
    62    
     62
    6363    /**
    64      * The factor to multiply with an easting coordinate to get from "easting 
     64     * The factor to multiply with an easting coordinate to get from "easting
    6565     * units per pixel" to "meters per pixel"
    6666     */
  • trunk/src/org/openstreetmap/josm/gui/BookmarkList.java

    r627 r1169  
    2121public class BookmarkList extends JList {
    2222
    23         /**
    24         * Create a bookmark list as well as the Buttons add and remove.
    25         */
    26         public BookmarkList() {
    27                 setModel(new DefaultListModel());
    28                 load();
    29                 setVisibleRowCount(7);
    30         }
     23    /**
     24    * Create a bookmark list as well as the Buttons add and remove.
     25    */
     26    public BookmarkList() {
     27        setModel(new DefaultListModel());
     28        load();
     29        setVisibleRowCount(7);
     30    }
    3131
    32         /**
    33         * Loads the bookmarks from file.
    34         */
    35         public void load() {
    36                 DefaultListModel model = (DefaultListModel)getModel();
    37                 model.removeAllElements();
    38                 try {
    39                         for (Preferences.Bookmark b : Main.pref.loadBookmarks())
    40                                 model.addElement(b);
    41                 } catch (IOException e) {
    42                         e.printStackTrace();
    43                         JOptionPane.showMessageDialog(Main.parent, tr("Could not read bookmarks.")+"\n"+e.getMessage());
    44                 }
    45         }
     32    /**
     33    * Loads the bookmarks from file.
     34    */
     35    public void load() {
     36        DefaultListModel model = (DefaultListModel)getModel();
     37        model.removeAllElements();
     38        try {
     39            for (Preferences.Bookmark b : Main.pref.loadBookmarks())
     40                model.addElement(b);
     41        } catch (IOException e) {
     42            e.printStackTrace();
     43            JOptionPane.showMessageDialog(Main.parent, tr("Could not read bookmarks.")+"\n"+e.getMessage());
     44        }
     45    }
    4646
    47         /**
    48         * Save all bookmarks to the preferences file
    49         */
    50         public void save() {
    51                 try {
    52                         Collection<Preferences.Bookmark> bookmarks = new LinkedList<Preferences.Bookmark>();
    53                         for (Object o : ((DefaultListModel)getModel()).toArray())
    54                                 bookmarks.add((Preferences.Bookmark)o);
    55                         Main.pref.saveBookmarks(bookmarks);
    56                 } catch (IOException e) {
    57                         JOptionPane.showMessageDialog(Main.parent,tr("Could not write bookmark.")+"\n"+e.getMessage());
    58                 }
    59         }
     47    /**
     48    * Save all bookmarks to the preferences file
     49    */
     50    public void save() {
     51        try {
     52            Collection<Preferences.Bookmark> bookmarks = new LinkedList<Preferences.Bookmark>();
     53            for (Object o : ((DefaultListModel)getModel()).toArray())
     54                bookmarks.add((Preferences.Bookmark)o);
     55            Main.pref.saveBookmarks(bookmarks);
     56        } catch (IOException e) {
     57            JOptionPane.showMessageDialog(Main.parent,tr("Could not write bookmark.")+"\n"+e.getMessage());
     58        }
     59    }
    6060}
  • trunk/src/org/openstreetmap/josm/gui/ConflictResolver.java

    r627 r1169  
    4646 * three tables in the screen, one for each both sides and one resulting table. The user can
    4747 * move items from either one of the sides ("my" and "their") to the resulting table.
    48  * 
     48 *
    4949 * @author Imi
    5050 */
    5151public class ConflictResolver extends JPanel {
    5252
    53         public static enum Resolution {MY, THEIR}
    54 
    55         private final class ConflictTableModel implements TableModel {
    56                 private final Resolution resolution;
    57                 public ConflictTableModel(Resolution resolution) {
    58                         this.resolution = resolution;
    59                 }
    60 
    61                 public int getRowCount() {
    62                         return conflicts.size();
    63                 }
    64 
    65                 public Object getValueAt(int rowIndex, int columnIndex) {
    66                         ConflictItem ci = conflicts.get(rowIndex);
    67                         if (columnIndex == 0)
    68                                 return ci.key();
    69                         Resolution r = resolution == null ? ci.resolution : resolution;
    70                         if (r == null)
    71                                 return "<html><i>???</i></html>";
    72                         JLabel l = new JLabel(r == Resolution.MY ? ci.my : ci.their);
    73                         if (ci.resolution == resolution && resolution != null)
    74                                 l.setFont(l.getFont().deriveFont(Font.BOLD));
    75                         return l;
    76                 }
    77 
    78                 public String getColumnName(int columnIndex) {return columnIndex == 0 ? tr("Key") : tr("Value");}
    79                 public int getColumnCount() {return 2;}
    80                 public boolean isCellEditable(int row, int column) {return false;}
    81                 public Class<?> getColumnClass(int columnIndex) {return Object.class;}
    82 
    83                 public void addTableModelListener(TableModelListener l) {}
    84                 public void removeTableModelListener(TableModelListener l) {}
    85                 public void setValueAt(Object aValue, int rowIndex, int columnIndex) {}
    86         }
    87 
    88         private final class DblClickListener extends MouseAdapter {
    89                 private final Resolution resolution;
    90                 public DblClickListener(Resolution resolution) {
    91                         this.resolution = resolution;
    92                 }
    93                 @Override public void mouseClicked(MouseEvent e) {
    94                         if (e.getClickCount() >= 2) {
    95                                 int sel = ((JTable)e.getSource()).getSelectedRow();
    96                                 if (sel == -1)
    97                                         return;
    98                                 ConflictResolver.this.conflicts.get(sel).resolution = resolution;
    99                                 repaint();
    100                         }
    101                 }
    102         }
    103         private final class ResolveAction extends AbstractAction {
    104                 private final Resolution resolution;
    105                 public ResolveAction(String name, Resolution resolution) {
    106                         super(null, ImageProvider.get("dialogs", name));
    107                         this.resolution = resolution;
    108                 }
    109                 public void actionPerformed(ActionEvent e) {
    110                         int sel = myTable.getSelectedRow();
    111                         if (sel == -1)
    112                                 return;
    113                         conflicts.get(sel).resolution = resolution;
    114                         if (sel == myTable.getRowCount()-1)
    115                                 myTable.clearSelection();
    116                         else
    117                                 myTable.getSelectionModel().setSelectionInterval(sel+1, sel+1);
    118                         repaint();
    119                 }
    120         }
    121 
    122         public final List<ConflictItem> conflicts = new ArrayList<ConflictItem>();
    123 
    124         private final ConflictTableModel my = new ConflictTableModel(Resolution.MY);
    125         private final JTable myTable;
    126         private final ConflictTableModel their = new ConflictTableModel(Resolution.THEIR);
    127         private final JTable theirTable;
    128         private final ConflictTableModel resolve = new ConflictTableModel(null);
    129         private final JTable resolveTable;
    130 
    131        
    132         public ConflictResolver(Map<OsmPrimitive, OsmPrimitive> conflicts) {
    133                 super(new GridBagLayout());
    134                 Collection<ConflictItem> possibleConflicts = new ArrayList<ConflictItem>();
    135                 possibleConflicts.add(new DeleteConflict());
    136                 possibleConflicts.add(new PositionConflict());
    137                 TreeSet<String> allkeys = new TreeSet<String>();
    138                 for (Entry<OsmPrimitive, OsmPrimitive> e : conflicts.entrySet()) {
    139                         allkeys.addAll(e.getKey().keySet());
    140                         allkeys.addAll(e.getValue().keySet());
    141                 }
    142                 for (String s : allkeys)
    143                         possibleConflicts.add(new PropertyConflict(s));
    144                
    145                 for (Entry<OsmPrimitive, OsmPrimitive> e : conflicts.entrySet()) {
    146                         for (Iterator<ConflictItem> it = possibleConflicts.iterator(); it.hasNext();) {
    147                                 ConflictItem ci = it.next();
    148                                 if (ci.hasConflict(e.getKey(), e.getValue())) {
    149                                         ci.initialize(conflicts);
    150                                         this.conflicts.add(ci);
    151                                         it.remove();
    152                                 }
    153                         }
    154                 }
    155                
    156 
    157                 // have to initialize the JTables here and not in the declaration, because its constructor
    158                 // may access this.conflicts (indirectly)
    159                 myTable = new JTable(my);
    160                 theirTable = new JTable(their);
    161                 resolveTable = new JTable(resolve);
    162                
    163                 myTable.setPreferredScrollableViewportSize(new Dimension(250,70));
    164                 theirTable.setPreferredScrollableViewportSize(new Dimension(250,70));
    165                 resolveTable.setPreferredScrollableViewportSize(new Dimension(250,70));
    166 
    167                 TableCellRenderer renderer = new DefaultTableCellRenderer(){
    168                         final Font defFont = new JLabel().getFont();
    169                         @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
    170                                 JLabel c = (JLabel)super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
    171                                 c.setIcon(null);
    172                                 c.setFont(defFont);
    173                                 if (value instanceof JLabel) {
    174                                         JLabel l = (JLabel)value;
    175                                         String text = l.getText();
    176                                         c.setText(text);
    177                                         c.setFont(l.getFont());
    178                                         if (text.startsWith("<html>") && l.getFont().isBold())
    179                                                 c.setText("<html>"+"<b>"+text.substring(6, text.length()-12));
    180                                 } else {
    181                                         String s = value.toString();
    182                                         int i = s.indexOf('|');
    183                                         if (i != -1) {
    184                                                 c.setIcon(ImageProvider.get("data", s.substring(0,i)));
    185                                                 c.setText(s.substring(i+1));
    186                                         }
    187                                 }
    188                                 return c;
    189                         }
    190                 };
    191                 myTable.setDefaultRenderer(Object.class, renderer);
    192                 theirTable.setDefaultRenderer(Object.class, renderer);
    193                 resolveTable.setDefaultRenderer(Object.class, renderer);
    194 
    195                 myTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
    196                 theirTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
    197                 resolveTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
    198                 ListSelectionListener selListener = new ListSelectionListener(){
    199                         public void valueChanged(ListSelectionEvent e) {
    200                                 if (((ListSelectionModel)e.getSource()).isSelectionEmpty()) {
    201                                         myTable.clearSelection();
    202                                         theirTable.clearSelection();
    203                                         resolveTable.clearSelection();
    204                                 } else {
    205                                         int i = ((ListSelectionModel)e.getSource()).getMinSelectionIndex();
    206                                         myTable.scrollRectToVisible(myTable.getCellRect(i, 0, true));
    207                                         myTable.getSelectionModel().setSelectionInterval(i, i);
    208                                         theirTable.scrollRectToVisible(theirTable.getCellRect(i, 0, true));
    209                                         theirTable.getSelectionModel().setSelectionInterval(i, i);
    210                                         resolveTable.scrollRectToVisible(resolveTable.getCellRect(i, 0, true));
    211                                         resolveTable.getSelectionModel().setSelectionInterval(i, i);
    212                                 }
    213                         }
    214                 };
    215                 myTable.getSelectionModel().addListSelectionListener(selListener);
    216                 theirTable.getSelectionModel().addListSelectionListener(selListener);
    217                 resolveTable.getSelectionModel().addListSelectionListener(selListener);
    218                 myTable.getSelectionModel().setSelectionInterval(0,0);
    219 
    220                 myTable.addMouseListener(new DblClickListener(Resolution.MY));
    221                 theirTable.addMouseListener(new DblClickListener(Resolution.THEIR));
    222                 resolveTable.addMouseListener(new DblClickListener(null));
    223 
    224                 add(new JLabel(trn("{0} object has conflicts:","{0} objects have conflicts:",conflicts.size(),conflicts.size())), GBC.eol().insets(0,0,0,10));
    225 
    226                 JPanel p = new JPanel(new GridBagLayout());
    227                 p.add(new JLabel(tr("my version:")), GBC.eol());
    228                 p.add(new JScrollPane(myTable), GBC.eol().fill(GBC.BOTH));
    229                 p.add(new JButton(new ResolveAction("down", Resolution.MY)), GBC.eol().anchor(GBC.CENTER).insets(0,5,0,0));
    230                 add(p, GBC.std().insets(0,0,5,0));
    231 
    232                 p = new JPanel(new GridBagLayout());
    233                 p.add(new JLabel(tr("their version:")), GBC.eol());
    234                 p.add(new JScrollPane(theirTable), GBC.eol().fill(GBC.BOTH));
    235                 p.add(new JButton(new ResolveAction("down", Resolution.THEIR)), GBC.eol().anchor(GBC.CENTER).insets(0,5,0,0));
    236                 add(p, GBC.eop().insets(5,0,0,0));
    237 
    238                 add(new JButton(new ResolveAction("up", null)), GBC.eol().anchor(GBC.CENTER));
    239                 add(new JLabel(tr("resolved version:")), GBC.eol().insets(0,5,0,0));
    240                 add(new JScrollPane(resolveTable), GBC.eol().anchor(GBC.CENTER).fill(GBC.BOTH));
    241         }
     53    public static enum Resolution {MY, THEIR}
     54
     55    private final class ConflictTableModel implements TableModel {
     56        private final Resolution resolution;
     57        public ConflictTableModel(Resolution resolution) {
     58            this.resolution = resolution;
     59        }
     60
     61        public int getRowCount() {
     62            return conflicts.size();
     63        }
     64
     65        public Object getValueAt(int rowIndex, int columnIndex) {
     66            ConflictItem ci = conflicts.get(rowIndex);
     67            if (columnIndex == 0)
     68                return ci.key();
     69            Resolution r = resolution == null ? ci.resolution : resolution;
     70            if (r == null)
     71                return "<html><i>???</i></html>";
     72            JLabel l = new JLabel(r == Resolution.MY ? ci.my : ci.their);
     73            if (ci.resolution == resolution && resolution != null)
     74                l.setFont(l.getFont().deriveFont(Font.BOLD));
     75            return l;
     76        }
     77
     78        public String getColumnName(int columnIndex) {return columnIndex == 0 ? tr("Key") : tr("Value");}
     79        public int getColumnCount() {return 2;}
     80        public boolean isCellEditable(int row, int column) {return false;}
     81        public Class<?> getColumnClass(int columnIndex) {return Object.class;}
     82
     83        public void addTableModelListener(TableModelListener l) {}
     84        public void removeTableModelListener(TableModelListener l) {}
     85        public void setValueAt(Object aValue, int rowIndex, int columnIndex) {}
     86    }
     87
     88    private final class DblClickListener extends MouseAdapter {
     89        private final Resolution resolution;
     90        public DblClickListener(Resolution resolution) {
     91            this.resolution = resolution;
     92        }
     93        @Override public void mouseClicked(MouseEvent e) {
     94            if (e.getClickCount() >= 2) {
     95                int sel = ((JTable)e.getSource()).getSelectedRow();
     96                if (sel == -1)
     97                    return;
     98                ConflictResolver.this.conflicts.get(sel).resolution = resolution;
     99                repaint();
     100            }
     101        }
     102    }
     103    private final class ResolveAction extends AbstractAction {
     104        private final Resolution resolution;
     105        public ResolveAction(String name, Resolution resolution) {
     106            super(null, ImageProvider.get("dialogs", name));
     107            this.resolution = resolution;
     108        }
     109        public void actionPerformed(ActionEvent e) {
     110            int sel = myTable.getSelectedRow();
     111            if (sel == -1)
     112                return;
     113            conflicts.get(sel).resolution = resolution;
     114            if (sel == myTable.getRowCount()-1)
     115                myTable.clearSelection();
     116            else
     117                myTable.getSelectionModel().setSelectionInterval(sel+1, sel+1);
     118            repaint();
     119        }
     120    }
     121
     122    public final List<ConflictItem> conflicts = new ArrayList<ConflictItem>();
     123
     124    private final ConflictTableModel my = new ConflictTableModel(Resolution.MY);
     125    private final JTable myTable;
     126    private final ConflictTableModel their = new ConflictTableModel(Resolution.THEIR);
     127    private final JTable theirTable;
     128    private final ConflictTableModel resolve = new ConflictTableModel(null);
     129    private final JTable resolveTable;
     130
     131
     132    public ConflictResolver(Map<OsmPrimitive, OsmPrimitive> conflicts) {
     133        super(new GridBagLayout());
     134        Collection<ConflictItem> possibleConflicts = new ArrayList<ConflictItem>();
     135        possibleConflicts.add(new DeleteConflict());
     136        possibleConflicts.add(new PositionConflict());
     137        TreeSet<String> allkeys = new TreeSet<String>();
     138        for (Entry<OsmPrimitive, OsmPrimitive> e : conflicts.entrySet()) {
     139            allkeys.addAll(e.getKey().keySet());
     140            allkeys.addAll(e.getValue().keySet());
     141        }
     142        for (String s : allkeys)
     143            possibleConflicts.add(new PropertyConflict(s));
     144
     145        for (Entry<OsmPrimitive, OsmPrimitive> e : conflicts.entrySet()) {
     146            for (Iterator<ConflictItem> it = possibleConflicts.iterator(); it.hasNext();) {
     147                ConflictItem ci = it.next();
     148                if (ci.hasConflict(e.getKey(), e.getValue())) {
     149                    ci.initialize(conflicts);
     150                    this.conflicts.add(ci);
     151                    it.remove();
     152                }
     153            }
     154        }
     155
     156
     157        // have to initialize the JTables here and not in the declaration, because its constructor
     158        // may access this.conflicts (indirectly)
     159        myTable = new JTable(my);
     160        theirTable = new JTable(their);
     161        resolveTable = new JTable(resolve);
     162
     163        myTable.setPreferredScrollableViewportSize(new Dimension(250,70));
     164        theirTable.setPreferredScrollableViewportSize(new Dimension(250,70));
     165        resolveTable.setPreferredScrollableViewportSize(new Dimension(250,70));
     166
     167        TableCellRenderer renderer = new DefaultTableCellRenderer(){
     168            final Font defFont = new JLabel().getFont();
     169            @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
     170                JLabel c = (JLabel)super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
     171                c.setIcon(null);
     172                c.setFont(defFont);
     173                if (value instanceof JLabel) {
     174                    JLabel l = (JLabel)value;
     175                    String text = l.getText();
     176                    c.setText(text);
     177                    c.setFont(l.getFont());
     178                    if (text.startsWith("<html>") && l.getFont().isBold())
     179                        c.setText("<html>"+"<b>"+text.substring(6, text.length()-12));
     180                } else {
     181                    String s = value.toString();
     182                    int i = s.indexOf('|');
     183                    if (i != -1) {
     184                        c.setIcon(ImageProvider.get("data", s.substring(0,i)));
     185                        c.setText(s.substring(i+1));
     186                    }
     187                }
     188                return c;
     189            }
     190        };
     191        myTable.setDefaultRenderer(Object.class, renderer);
     192        theirTable.setDefaultRenderer(Object.class, renderer);
     193        resolveTable.setDefaultRenderer(Object.class, renderer);
     194
     195        myTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
     196        theirTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
     197        resolveTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
     198        ListSelectionListener selListener = new ListSelectionListener(){
     199            public void valueChanged(ListSelectionEvent e) {
     200                if (((ListSelectionModel)e.getSource()).isSelectionEmpty()) {
     201                    myTable.clearSelection();
     202                    theirTable.clearSelection();
     203                    resolveTable.clearSelection();
     204                } else {
     205                    int i = ((ListSelectionModel)e.getSource()).getMinSelectionIndex();
     206                    myTable.scrollRectToVisible(myTable.getCellRect(i, 0, true));
     207                    myTable.getSelectionModel().setSelectionInterval(i, i);
     208                    theirTable.scrollRectToVisible(theirTable.getCellRect(i, 0, true));
     209                    theirTable.getSelectionModel().setSelectionInterval(i, i);
     210                    resolveTable.scrollRectToVisible(resolveTable.getCellRect(i, 0, true));
     211                    resolveTable.getSelectionModel().setSelectionInterval(i, i);
     212                }
     213            }
     214        };
     215        myTable.getSelectionModel().addListSelectionListener(selListener);
     216        theirTable.getSelectionModel().addListSelectionListener(selListener);
     217        resolveTable.getSelectionModel().addListSelectionListener(selListener);
     218        myTable.getSelectionModel().setSelectionInterval(0,0);
     219
     220        myTable.addMouseListener(new DblClickListener(Resolution.MY));
     221        theirTable.addMouseListener(new DblClickListener(Resolution.THEIR));
     222        resolveTable.addMouseListener(new DblClickListener(null));
     223
     224        add(new JLabel(trn("{0} object has conflicts:","{0} objects have conflicts:",conflicts.size(),conflicts.size())), GBC.eol().insets(0,0,0,10));
     225
     226        JPanel p = new JPanel(new GridBagLayout());
     227        p.add(new JLabel(tr("my version:")), GBC.eol());
     228        p.add(new JScrollPane(myTable), GBC.eol().fill(GBC.BOTH));
     229        p.add(new JButton(new ResolveAction("down", Resolution.MY)), GBC.eol().anchor(GBC.CENTER).insets(0,5,0,0));
     230        add(p, GBC.std().insets(0,0,5,0));
     231
     232        p = new JPanel(new GridBagLayout());
     233        p.add(new JLabel(tr("their version:")), GBC.eol());
     234        p.add(new JScrollPane(theirTable), GBC.eol().fill(GBC.BOTH));
     235        p.add(new JButton(new ResolveAction("down", Resolution.THEIR)), GBC.eol().anchor(GBC.CENTER).insets(0,5,0,0));
     236        add(p, GBC.eop().insets(5,0,0,0));
     237
     238        add(new JButton(new ResolveAction("up", null)), GBC.eol().anchor(GBC.CENTER));
     239        add(new JLabel(tr("resolved version:")), GBC.eol().insets(0,5,0,0));
     240        add(new JScrollPane(resolveTable), GBC.eol().anchor(GBC.CENTER).fill(GBC.BOTH));
     241    }
    242242}
  • trunk/src/org/openstreetmap/josm/gui/GettingStarted.java

    r1163 r1169  
    2525public class GettingStarted extends JPanel {
    2626
    27     static private String content = "";   
     27    static private String content = "";
    2828
    2929    public class LinkGeneral extends JEditorPane implements HyperlinkListener {
     
    7474                boolean included = false;
    7575                if (condition.equals("%3E")) {
    76                     if ((myVersion == 0 || myVersion > targetVersion) 
     76                    if ((myVersion == 0 || myVersion > targetVersion)
    7777                        /* && ! Main.pref.getBoolean("motd.gt."+targetVersion) */) {
    7878                        /* Main.pref.put("motd.gt."+targetVersion, true); */
     
    8080                    }
    8181                } else if (condition.equals("%3E%3D")) {
    82                     if ((myVersion == 0 || myVersion >= targetVersion) 
     82                    if ((myVersion == 0 || myVersion >= targetVersion)
    8383                        /* && ! Main.pref.getBoolean("motd.ge."+targetVersion) */) {
    8484                        /* Main.pref.put("motd.ge."+targetVersion, true); */
     
    114114                            content += wr.read(url).replace("<html>", "").replace("</html>", "").replace("<div id=\"searchable\">", "").replace("</div>", "");
    115115                        } catch (IOException ioe2) {
    116                         }           
    117                     }           
     116                        }
     117                    }
    118118                }
    119119            }
    120120            content += motdcontent.substring(start);
    121121            content = content.replace("<html>", "<html><style type=\"text/css\">\nbody { font-family: sans-serif; font-weight: bold; }\nh1 {text-align: center;}</style>");
    122             /* replace the wiki title */ 
    123             content = content.replace("JOSM, the Java OpenStreetMap editor", tr("JOSM, the Java OpenStreetMap editor")); 
     122            /* replace the wiki title */
     123            content = content.replace("JOSM, the Java OpenStreetMap editor", tr("JOSM, the Java OpenStreetMap editor"));
    124124        }
    125125
    126126    }
    127    
     127
    128128    public GettingStarted() {
    129129        super(new BorderLayout());
    130130        assignContent();
    131                                
     131
    132132        // panel.add(GBC.glue(0,1), GBC.eol());
    133133        //panel.setMinimumSize(new Dimension(400, 600));
  • trunk/src/org/openstreetmap/josm/gui/IconToggleButton.java

    r627 r1169  
    1818public class IconToggleButton extends JToggleButton implements PropertyChangeListener {
    1919
    20         public boolean groupbutton;
     20    public boolean groupbutton;
    2121
    22         /**
    23         * Construct the toggle button with the given action.
    24         */
    25         public IconToggleButton(Action action) {
    26                 super(action);
    27                 setText(null);
     22    /**
     23    * Construct the toggle button with the given action.
     24    */
     25    public IconToggleButton(Action action) {
     26        super(action);
     27        setText(null);
    2828
    29                 Object o = action.getValue(Action.SHORT_DESCRIPTION);
    30                 if (o != null)
    31                         setToolTipText(o.toString());
     29        Object o = action.getValue(Action.SHORT_DESCRIPTION);
     30        if (o != null)
     31            setToolTipText(o.toString());
    3232
    33                 action.addPropertyChangeListener(this);
    34                
    35                 addMouseListener(new MouseAdapter(){
    36                         @Override public void mousePressed(MouseEvent e) {
    37                                 groupbutton = e.getX() > getWidth()/2 && e.getY() > getHeight()/2;
     33        action.addPropertyChangeListener(this);
     34
     35        addMouseListener(new MouseAdapter(){
     36            @Override public void mousePressed(MouseEvent e) {
     37                groupbutton = e.getX() > getWidth()/2 && e.getY() > getHeight()/2;
    3838            }
    39                 });
    40         }
     39        });
     40    }
    4141
    42         public void propertyChange(PropertyChangeEvent evt) {
    43                 if (evt.getPropertyName().equals("active")) {
    44                         setSelected((Boolean)evt.getNewValue());
    45                         requestFocusInWindow();
    46                 }
    47         }
     42    public void propertyChange(PropertyChangeEvent evt) {
     43        if (evt.getPropertyName().equals("active")) {
     44            setSelected((Boolean)evt.getNewValue());
     45            requestFocusInWindow();
     46        }
     47    }
    4848}
  • trunk/src/org/openstreetmap/josm/gui/JMultilineLabel.java

    r729 r1169  
    11// License: GPL. For details, see LICENSE file.
    22
    3 // This class was taken from 
     3// This class was taken from
    44// http://forum.java.sun.com/thread.jspa?threadID=459705&messageID=2104021
    55// - Removed hardcoded margin
     
    2222
    2323public class JMultilineLabel extends JComponent {
    24         private String text;
    25         private int maxWidth = Integer.MAX_VALUE;
    26         private boolean justify;
    27         private final FontRenderContext frc = new FontRenderContext(null, false, false);
     24    private String text;
     25    private int maxWidth = Integer.MAX_VALUE;
     26    private boolean justify;
     27    private final FontRenderContext frc = new FontRenderContext(null, false, false);
    2828
    29         public JMultilineLabel(String description) {
    30                 super();
    31                 setText(description);
    32         }
     29    public JMultilineLabel(String description) {
     30        super();
     31        setText(description);
     32    }
    3333
    34         private void morph() {
    35                 revalidate();
    36                 repaint();
    37         }
     34    private void morph() {
     35        revalidate();
     36        repaint();
     37    }
    3838
    39         public String getText() {
    40                 return text;
    41         }
     39    public String getText() {
     40        return text;
     41    }
    4242
    43         public void setText(String text) {
    44                 String old = this.text;
    45                 this.text = text;
    46                 firePropertyChange("text", old, this.text);
    47                 if ((old == null) ? text!=null : !old.equals(text))
    48                         morph();
    49         }
     43    public void setText(String text) {
     44        String old = this.text;
     45        this.text = text;
     46        firePropertyChange("text", old, this.text);
     47        if ((old == null) ? text!=null : !old.equals(text))
     48            morph();
     49    }
    5050
    51         public int getMaxWidth() {
    52                 return maxWidth;
    53         }
     51    public int getMaxWidth() {
     52        return maxWidth;
     53    }
    5454
    55         public void setMaxWidth(int maxWidth) {
    56                 if (maxWidth <= 0)
    57                         throw new IllegalArgumentException();
    58                 int old = this.maxWidth;
    59                 this.maxWidth = maxWidth;
    60                 firePropertyChange("maxWidth", old, this.maxWidth);
    61                 if (old !=      this.maxWidth)
    62                         morph();
    63         }
     55    public void setMaxWidth(int maxWidth) {
     56        if (maxWidth <= 0)
     57            throw new IllegalArgumentException();
     58        int old = this.maxWidth;
     59        this.maxWidth = maxWidth;
     60        firePropertyChange("maxWidth", old, this.maxWidth);
     61        if (old !=  this.maxWidth)
     62            morph();
     63    }
    6464
    65         public boolean isJustified() {
    66                 return justify;
    67         }
     65    public boolean isJustified() {
     66        return justify;
     67    }
    6868
    69         public void setJustified(boolean justify) {
    70                 boolean old = this.justify;
    71                 this.justify = justify;
    72                 firePropertyChange("justified", old, this.justify);
    73                 if (old != this.justify)
    74                         repaint();
    75         }
     69    public void setJustified(boolean justify) {
     70        boolean old = this.justify;
     71        this.justify = justify;
     72        firePropertyChange("justified", old, this.justify);
     73        if (old != this.justify)
     74            repaint();
     75    }
    7676
    77         public Dimension getPreferredSize() {
    78                 return paintOrGetSize(null, getMaxWidth());
    79         }
     77    public Dimension getPreferredSize() {
     78        return paintOrGetSize(null, getMaxWidth());
     79    }
    8080
    81         public Dimension getMinimumSize() {
    82                 return getPreferredSize();
    83         }
     81    public Dimension getMinimumSize() {
     82        return getPreferredSize();
     83    }
    8484
    85         protected void paintComponent(Graphics g) {
    86                 super.paintComponent(g);
    87                 paintOrGetSize((Graphics2D)g, getWidth());
    88         }
     85    protected void paintComponent(Graphics g) {
     86        super.paintComponent(g);
     87        paintOrGetSize((Graphics2D)g, getWidth());
     88    }
    8989
    90         private Dimension paintOrGetSize(Graphics2D g, int width) {
    91                 Insets insets = getInsets();
    92                 width -= insets.left + insets.right;
    93                 float w = insets.left + insets.right;
    94                 float x = insets.left, y=insets.top;
    95                 if (width > 0 && text != null && text.length() > 0) {
    96                         AttributedString as = new AttributedString(getText());
    97                         as.addAttribute(TextAttribute.FONT, getFont());
    98                         AttributedCharacterIterator aci = as.getIterator();
    99                         LineBreakMeasurer lbm = new LineBreakMeasurer(aci, frc);
    100                         float max = 0;
    101                         while (lbm.getPosition() < aci.getEndIndex()) {
    102                                 TextLayout textLayout = lbm.nextLayout(width);
    103                                 if (g != null && isJustified() && textLayout.getVisibleAdvance() > 0.80 * width)
    104                                         textLayout = textLayout.getJustifiedLayout(width);
    105                                 if (g != null)
    106                                         textLayout.draw(g, x, y + textLayout.getAscent());
    107                                 y += textLayout.getDescent() + textLayout.getLeading() + textLayout.getAscent();
    108                                 max = Math.max(max, textLayout.getVisibleAdvance());
    109                         }
    110                         w += max;
    111                 }
    112                 return new Dimension((int)Math.ceil(w), (int)Math.ceil(y) + insets.bottom);
    113         }
     90    private Dimension paintOrGetSize(Graphics2D g, int width) {
     91        Insets insets = getInsets();
     92        width -= insets.left + insets.right;
     93        float w = insets.left + insets.right;
     94        float x = insets.left, y=insets.top;
     95        if (width > 0 && text != null && text.length() > 0) {
     96            AttributedString as = new AttributedString(getText());
     97            as.addAttribute(TextAttribute.FONT, getFont());
     98            AttributedCharacterIterator aci = as.getIterator();
     99            LineBreakMeasurer lbm = new LineBreakMeasurer(aci, frc);
     100            float max = 0;
     101            while (lbm.getPosition() < aci.getEndIndex()) {
     102                TextLayout textLayout = lbm.nextLayout(width);
     103                if (g != null && isJustified() && textLayout.getVisibleAdvance() > 0.80 * width)
     104                    textLayout = textLayout.getJustifiedLayout(width);
     105                if (g != null)
     106                    textLayout.draw(g, x, y + textLayout.getAscent());
     107                y += textLayout.getDescent() + textLayout.getLeading() + textLayout.getAscent();
     108                max = Math.max(max, textLayout.getVisibleAdvance());
     109            }
     110            w += max;
     111        }
     112        return new Dimension((int)Math.ceil(w), (int)Math.ceil(y) + insets.bottom);
     113    }
    114114}
  • trunk/src/org/openstreetmap/josm/gui/MainApplet.java

    r1146 r1169  
    3333public class MainApplet extends JApplet {
    3434
    35         public static final class UploadPreferencesAction extends JosmAction {
    36                 public UploadPreferencesAction() {
    37                         super(tr("Upload Preferences"), "upload-preferences", tr("Upload the current preferences to the server"),
    38                         Shortcut.registerShortcut("applet:uploadprefs", tr("Upload Preferences"), KeyEvent.VK_U, Shortcut.GROUP_HOTKEY), true);
     35    public static final class UploadPreferencesAction extends JosmAction {
     36        public UploadPreferencesAction() {
     37            super(tr("Upload Preferences"), "upload-preferences", tr("Upload the current preferences to the server"),
     38            Shortcut.registerShortcut("applet:uploadprefs", tr("Upload Preferences"), KeyEvent.VK_U, Shortcut.GROUP_HOTKEY), true);
    3939        }
    40             public void actionPerformed(ActionEvent e) {
    41                 ((ServerSidePreferences)Main.pref).upload();
    42             }
     40        public void actionPerformed(ActionEvent e) {
     41            ((ServerSidePreferences)Main.pref).upload();
     42        }
    4343    }
    4444
    4545    private final class MainCaller extends Main {
    46                 private MainCaller() {
    47                         setContentPane(contentPane);
    48                         setJMenuBar(menu);
    49                         setBounds(bounds);
    50                 }
    51         }
     46        private MainCaller() {
     47            setContentPane(contentPane);
     48            setJMenuBar(menu);
     49            setBounds(bounds);
     50        }
     51    }
    5252
    53         private final static String[][] paramInfo = {
    54                 {"username", tr("string"), tr("Name of the user.")},
    55                 {"password", tr("string"), tr("OSM Password.")},
    56                 {"geometry", tr("string"), tr("Resize the applet to the given geometry (format: WIDTHxHEIGHT)")},
    57                 {"download", tr("string;string;..."), tr("Download each. Can be x1,y1,x2,y2 an url containing lat=y&lon=x&zoom=z or a filename")},
    58                 {"downloadgps", tr("string;string;..."), tr("Download each as raw gps. Can be x1,y1,x2,y2 an url containing lat=y&lon=x&zoom=z or a filename")},
    59                 {"selection", tr("string;string;..."), tr("Add each to the initial selection. Can be a google-like search string or an url which returns osm-xml")},
    60                 {"reset-preferences", tr("any"),tr("If specified, reset the configuration instead of reading it.")}
    61         };
     53    private final static String[][] paramInfo = {
     54        {"username", tr("string"), tr("Name of the user.")},
     55        {"password", tr("string"), tr("OSM Password.")},
     56        {"geometry", tr("string"), tr("Resize the applet to the given geometry (format: WIDTHxHEIGHT)")},
     57        {"download", tr("string;string;..."), tr("Download each. Can be x1,y1,x2,y2 an url containing lat=y&lon=x&zoom=z or a filename")},
     58        {"downloadgps", tr("string;string;..."), tr("Download each as raw gps. Can be x1,y1,x2,y2 an url containing lat=y&lon=x&zoom=z or a filename")},
     59        {"selection", tr("string;string;..."), tr("Add each to the initial selection. Can be a google-like search string or an url which returns osm-xml")},
     60        {"reset-preferences", tr("any"),tr("If specified, reset the configuration instead of reading it.")}
     61    };
    6262
    63         private Map<String, Collection<String>> args = new HashMap<String, Collection<String>>();
     63    private Map<String, Collection<String>> args = new HashMap<String, Collection<String>>();
    6464
    65         @Override public String[][] getParameterInfo() {
    66                 return paramInfo;
    67         }
     65    @Override public String[][] getParameterInfo() {
     66        return paramInfo;
     67    }
    6868
    69         @Override public void init() {
    70                 for (String[] s : paramInfo) {
    71                         Collection<String> p = readParameter(s[0], args.get(s[0]));
    72                         if (p != null)
    73                                 args.put(s[0], p);
    74                 }
    75                 if (!args.containsKey("geometry") && getParameter("width") != null && getParameter("height") != null) {
    76                         args.put("geometry", Arrays.asList(new String[]{getParameter("width")+"x"+getParameter("height")}));
    77                 }
    78         }
     69    @Override public void init() {
     70        for (String[] s : paramInfo) {
     71            Collection<String> p = readParameter(s[0], args.get(s[0]));
     72            if (p != null)
     73                args.put(s[0], p);
     74        }
     75        if (!args.containsKey("geometry") && getParameter("width") != null && getParameter("height") != null) {
     76            args.put("geometry", Arrays.asList(new String[]{getParameter("width")+"x"+getParameter("height")}));
     77        }
     78    }
    7979
    80         @Override public void start() {
    81                 String username = args.containsKey("username") ? args.get("username").iterator().next() : null;
    82                 String password = args.containsKey("password") ? args.get("password").iterator().next() : null;
    83                 if (username == null || password == null) {
    84                         JPanel p = new JPanel(new GridBagLayout());
    85                         p.add(new JLabel(tr("Username")), GBC.std().insets(0,0,20,0));
    86                         JTextField user = new JTextField(username == null ? "" : username);
    87                         p.add(user, GBC.eol().fill(GBC.HORIZONTAL));
    88                         p.add(new JLabel(tr("Password")), GBC.std().insets(0,0,20,0));
    89                         JPasswordField pass = new JPasswordField(password == null ? "" : password);
    90                         p.add(pass, GBC.eol().fill(GBC.HORIZONTAL));
    91                         JOptionPane.showMessageDialog(null, p);
    92                         username = user.getText();
    93                         password = new String(pass.getPassword());
    94                         args.put("password", Arrays.asList(new String[]{password}));
    95                 }
     80    @Override public void start() {
     81        String username = args.containsKey("username") ? args.get("username").iterator().next() : null;
     82        String password = args.containsKey("password") ? args.get("password").iterator().next() : null;
     83        if (username == null || password == null) {
     84            JPanel p = new JPanel(new GridBagLayout());
     85            p.add(new JLabel(tr("Username")), GBC.std().insets(0,0,20,0));
     86            JTextField user = new JTextField(username == null ? "" : username);
     87            p.add(user, GBC.eol().fill(GBC.HORIZONTAL));
     88            p.add(new JLabel(tr("Password")), GBC.std().insets(0,0,20,0));
     89            JPasswordField pass = new JPasswordField(password == null ? "" : password);
     90            p.add(pass, GBC.eol().fill(GBC.HORIZONTAL));
     91            JOptionPane.showMessageDialog(null, p);
     92            username = user.getText();
     93            password = new String(pass.getPassword());
     94            args.put("password", Arrays.asList(new String[]{password}));
     95        }
    9696
    97                 Main.applet = true;
    98                 Main.pref = new ServerSidePreferences(getCodeBase());
    99                 ((ServerSidePreferences)Main.pref).download(username, password);
     97        Main.applet = true;
     98        Main.pref = new ServerSidePreferences(getCodeBase());
     99        ((ServerSidePreferences)Main.pref).download(username, password);
    100100
    101                 Main.preConstructorInit(args);
    102                 Main.parent = this;
    103                 new MainCaller().postConstructorProcessCmdLine(args);
     101        Main.preConstructorInit(args);
     102        Main.parent = this;
     103        new MainCaller().postConstructorProcessCmdLine(args);
    104104
    105                 MainMenu m = Main.main.menu; // shortcut
     105        MainMenu m = Main.main.menu; // shortcut
    106106
    107                 // remove offending stuff from JOSM (that would break the SecurityManager)
    108                 m.remove(m.fileMenu);
    109                 m.editMenu.add(new UploadPreferencesAction());
    110                 m.openFile.setEnabled(false);
    111                 m.exit.setEnabled(false);
    112                 m.save.setEnabled(false);
    113                 m.saveAs.setEnabled(false);
    114                 m.gpxExport.setEnabled(false);
    115         }
     107        // remove offending stuff from JOSM (that would break the SecurityManager)
     108        m.remove(m.fileMenu);
     109        m.editMenu.add(new UploadPreferencesAction());
     110        m.openFile.setEnabled(false);
     111        m.exit.setEnabled(false);
     112        m.save.setEnabled(false);
     113        m.saveAs.setEnabled(false);
     114        m.gpxExport.setEnabled(false);
     115    }
    116116
    117         private Collection<String> readParameter(String s, Collection<String> v) {
    118                 String param = getParameter(s);
    119                 if (param != null) {
    120                         if (v == null)
    121                                 v = new LinkedList<String>();
    122                         v.addAll(Arrays.asList(param.split(";")));
    123                 }
    124                 return v;
    125         }
     117    private Collection<String> readParameter(String s, Collection<String> v) {
     118        String param = getParameter(s);
     119        if (param != null) {
     120            if (v == null)
     121                v = new LinkedList<String>();
     122            v.addAll(Arrays.asList(param.split(";")));
     123        }
     124        return v;
     125    }
    126126
    127         public static void main(String[] args) {
    128                 final JFrame frame = new JFrame("Java OpenStreetMap Applet");
    129                 MainApplet applet = new MainApplet();
    130                 applet.setStub(new AppletStub() {
    131                         public void appletResize(int w, int h) {
    132                                 frame.setSize(w, h);
    133                         }
     127    public static void main(String[] args) {
     128        final JFrame frame = new JFrame("Java OpenStreetMap Applet");
     129        MainApplet applet = new MainApplet();
     130        applet.setStub(new AppletStub() {
     131            public void appletResize(int w, int h) {
     132                frame.setSize(w, h);
     133            }
    134134
    135                         public AppletContext getAppletContext() {
    136                                 return null;
    137                         }
     135            public AppletContext getAppletContext() {
     136                return null;
     137            }
    138138
    139                         public URL getCodeBase() {
    140                                 try {
    141                                         return new File(".").toURI().toURL();
    142                                 } catch (Exception e) {
    143                                         e.printStackTrace();
    144                                         return null;
    145                                 }
    146                         }
     139            public URL getCodeBase() {
     140                try {
     141                    return new File(".").toURI().toURL();
     142                } catch (Exception e) {
     143                    e.printStackTrace();
     144                    return null;
     145                }
     146            }
    147147
    148                         public URL getDocumentBase() {
    149                                 return getCodeBase();
    150                         }
     148            public URL getDocumentBase() {
     149                return getCodeBase();
     150            }
    151151
    152                         public String getParameter(String k) {
    153                                 return null;
    154                         }
     152            public String getParameter(String k) {
     153                return null;
     154            }
    155155
    156                         public boolean isActive() {
    157                                 return true;
    158                         }
    159                 });
    160                 applet.init();
    161                 applet.start();
    162                 frame.setContentPane(applet);
    163                 frame.setVisible(true);
    164         }
     156            public boolean isActive() {
     157                return true;
     158            }
     159        });
     160        applet.init();
     161        applet.start();
     162        frame.setContentPane(applet);
     163        frame.setVisible(true);
     164    }
    165165}
  • trunk/src/org/openstreetmap/josm/gui/MainApplication.java

    r1082 r1169  
    3737 */
    3838public class MainApplication extends Main {
    39         /**
    40         * Allow subclassing (see JOSM.java)
    41         */
    42         public MainApplication() {}
    43 
    44         /**
    45         * Construct an main frame, ready sized and operating. Does not
    46         * display the frame.
    47         */
    48         public MainApplication(JFrame mainFrame) {
    49                 super();
    50                 mainFrame.setContentPane(contentPane);
    51                 mainFrame.setJMenuBar(menu);
    52                 mainFrame.setBounds(bounds);
    53                 mainFrame.setIconImage(ImageProvider.get("logo.png").getImage());
    54                 mainFrame.addWindowListener(new WindowAdapter(){
    55                         @Override public void windowClosing(final WindowEvent arg0) {
    56                                 if (Main.breakBecauseUnsavedChanges())
    57                                         return;
    58                                 System.exit(0);
    59                         }
    60                 });
    61                 mainFrame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
    62         }
    63 
    64         /**
    65         * Main application Startup
    66         */
    67         public static void main(final String[] argArray) {
    68                 /////////////////////////////////////////////////////////////////////////
    69                 //                        TO ALL TRANSLATORS
    70                 /////////////////////////////////////////////////////////////////////////
    71                 // Do not translate the early strings below until the locale is set up.
    72                 // (By the eager loaded plugins)
    73                 //
    74                 // These strings cannot be translated. That's life. Really. Sorry.
    75                 //
    76                 //                                                                 Imi.
    77                 /////////////////////////////////////////////////////////////////////////
    78 
    79                 Thread.setDefaultUncaughtExceptionHandler(new BugReportExceptionHandler());
    80 
    81                 // initialize the plaform hook, and
    82                 Main.determinePlatformHook();
    83                 // call the really early hook before we anything else
    84                 Main.platform.preStartupHook();
    85 
    86                 // construct argument table
    87                 List<String> argList = Arrays.asList(argArray);
    88                 final Map<String, Collection<String>> args = new HashMap<String, Collection<String>>();
    89                 for (String arg : argArray) {
    90                         if (!arg.startsWith("--"))
    91                                 arg = "--download="+arg;
    92                         int i = arg.indexOf('=');
    93                         String key = i == -1 ? arg.substring(2) : arg.substring(2,i);
    94                         String value = i == -1 ? "" : arg.substring(i+1);
    95                         Collection<String> v = args.get(key);
    96                         if (v == null)
    97                                 v = new LinkedList<String>();
    98                         v.add(value);
    99                         args.put(key, v);
    100                 }
    101 
    102                 if (argList.contains("--help") || argList.contains("-?") || argList.contains("-h")) {
    103                         // TODO: put in a platformHook for system that have no console by default
    104                         System.out.println(tr("Java OpenStreetMap Editor")+"\n\n"+
    105                                         tr("usage")+":\n"+
    106                                         "\tjava -jar josm.jar <option> <option> <option>...\n\n"+
    107                                         tr("options")+":\n"+
    108                                         "\t--help|-?|-h                              "+tr("Show this help")+"\n"+
    109                                         "\t--geometry=widthxheight(+|-)x(+|-)y       "+tr("Standard unix geometry argument")+"\n"+
    110                                         "\t[--download=]minlat,minlon,maxlat,maxlon  "+tr("Download the bounding box")+"\n"+
    111                                         "\t[--download=]<url>                        "+tr("Download the location at the url (with lat=x&lon=y&zoom=z)")+"\n"+
    112                                         "\t[--download=]<filename>                   "+tr("Open file (as raw gps, if .gpx)")+"\n"+
    113                                         "\t--downloadgps=minlat,minlon,maxlat,maxlon "+tr("Download the bounding box as raw gps")+"\n"+
    114                                         "\t--selection=<searchstring>                "+tr("Select with the given search")+"\n"+
    115                                         "\t--no-fullscreen                           "+tr("Don't launch in fullscreen mode")+"\n"+
    116                                         "\t--reset-preferences                       "+tr("Reset the preferences to default")+"\n\n"+
    117                                         "\t--language=<language>                     "+tr("Set the language.")+"\n\n"+
    118                                         tr("examples")+":\n"+
    119                                         "\tjava -jar josm.jar track1.gpx track2.gpx london.osm\n"+
    120                                         "\tjava -jar josm.jar http://www.openstreetmap.org/index.html?lat=43.2&lon=11.1&zoom=13\n"+
    121                                         "\tjava -jar josm.jar london.osm --selection=http://www.ostertag.name/osm/OSM_errors_node-duplicate.xml\n"+
    122                                         "\tjava -jar josm.jar 43.2,11.1,43.4,11.4\n\n"+
    123 
    124                                         tr("Parameters are read in the order they are specified, so make sure you load\n"+
    125                                         "some data before --selection")+"\n\n"+
    126                                         tr("Instead of --download=<bbox> you may specify osm://<bbox>\n"));
    127                         System.exit(0);
    128                 }
    129 
    130                 // get the preferences.
    131                 final File prefDir = new File(Main.pref.getPreferencesDir());
    132                 // check if preferences directory has moved (TODO: Update code. Remove this after some time)
    133                 File oldPrefDir = new File(System.getProperty("user.home"), ".josm");
    134                 if (!prefDir.isDirectory() && oldPrefDir.isDirectory()) {
    135                         if (oldPrefDir.renameTo(prefDir)) {
    136                                 // do not translate this
    137                                 JOptionPane.showMessageDialog(null, "The preference directory has been moved to "+prefDir);
    138                         } else {
    139                                 JOptionPane.showMessageDialog(null, "The preference directory location has changed. Please move "+oldPrefDir+" to "+prefDir);
    140                         }
    141                 }
    142 
    143                 if (prefDir.exists() && !prefDir.isDirectory()) {
    144                         JOptionPane.showMessageDialog(null, "Cannot open preferences directory: "+Main.pref.getPreferencesDir());
    145                         return;
    146                 }
    147                 if (!prefDir.exists())
    148                         prefDir.mkdirs();
    149 
    150                 if (!new File(Main.pref.getPreferencesDir()+"preferences").exists()) {
    151                         Main.pref.resetToDefault();
    152                 }
    153 
    154                 try {
    155                         if (args.containsKey("reset-preferences")) {
    156                                 Main.pref.resetToDefault();
    157                         } else {
    158                                 Main.pref.load();
    159                         }
    160                 } catch (final IOException e1) {
    161                         e1.printStackTrace();
    162                         String backup = Main.pref.getPreferencesDir() + "preferences.bak";
    163                         JOptionPane.showMessageDialog(null, "Preferences file had errors.  Making backup of old one to " + backup);
    164                         new File(Main.pref.getPreferencesDir() + "preferences").renameTo(new File(backup));
    165                         Main.pref.save();
    166                 }
     39    /**
     40    * Allow subclassing (see JOSM.java)
     41    */
     42    public MainApplication() {}
     43
     44    /**
     45    * Construct an main frame, ready sized and operating. Does not
     46    * display the frame.
     47    */
     48    public MainApplication(JFrame mainFrame) {
     49        super();
     50        mainFrame.setContentPane(contentPane);
     51        mainFrame.setJMenuBar(menu);
     52        mainFrame.setBounds(bounds);
     53        mainFrame.setIconImage(ImageProvider.get("logo.png").getImage());
     54        mainFrame.addWindowListener(new WindowAdapter(){
     55            @Override public void windowClosing(final WindowEvent arg0) {
     56                if (Main.breakBecauseUnsavedChanges())
     57                    return;
     58                System.exit(0);
     59            }
     60        });
     61        mainFrame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
     62    }
     63
     64    /**
     65    * Main application Startup
     66    */
     67    public static void main(final String[] argArray) {
     68        /////////////////////////////////////////////////////////////////////////
     69        //                        TO ALL TRANSLATORS
     70        /////////////////////////////////////////////////////////////////////////
     71        // Do not translate the early strings below until the locale is set up.
     72        // (By the eager loaded plugins)
     73        //
     74        // These strings cannot be translated. That's life. Really. Sorry.
     75        //
     76        //                                                                 Imi.
     77        /////////////////////////////////////////////////////////////////////////
     78
     79        Thread.setDefaultUncaughtExceptionHandler(new BugReportExceptionHandler());
     80
     81        // initialize the plaform hook, and
     82        Main.determinePlatformHook();
     83        // call the really early hook before we anything else
     84        Main.platform.preStartupHook();
     85
     86        // construct argument table
     87        List<String> argList = Arrays.asList(argArray);
     88        final Map<String, Collection<String>> args = new HashMap<String, Collection<String>>();
     89        for (String arg : argArray) {
     90            if (!arg.startsWith("--"))
     91                arg = "--download="+arg;
     92            int i = arg.indexOf('=');
     93            String key = i == -1 ? arg.substring(2) : arg.substring(2,i);
     94            String value = i == -1 ? "" : arg.substring(i+1);
     95            Collection<String> v = args.get(key);
     96            if (v == null)
     97                v = new LinkedList<String>();
     98            v.add(value);
     99            args.put(key, v);
     100        }
     101
     102        if (argList.contains("--help") || argList.contains("-?") || argList.contains("-h")) {
     103            // TODO: put in a platformHook for system that have no console by default
     104            System.out.println(tr("Java OpenStreetMap Editor")+"\n\n"+
     105                    tr("usage")+":\n"+
     106                    "\tjava -jar josm.jar <option> <option> <option>...\n\n"+
     107                    tr("options")+":\n"+
     108                    "\t--help|-?|-h                              "+tr("Show this help")+"\n"+
     109                    "\t--geometry=widthxheight(+|-)x(+|-)y       "+tr("Standard unix geometry argument")+"\n"+
     110                    "\t[--download=]minlat,minlon,maxlat,maxlon  "+tr("Download the bounding box")+"\n"+
     111                    "\t[--download=]<url>                        "+tr("Download the location at the url (with lat=x&lon=y&zoom=z)")+"\n"+
     112                    "\t[--download=]<filename>                   "+tr("Open file (as raw gps, if .gpx)")+"\n"+
     113                    "\t--downloadgps=minlat,minlon,maxlat,maxlon "+tr("Download the bounding box as raw gps")+"\n"+
     114                    "\t--selection=<searchstring>                "+tr("Select with the given search")+"\n"+
     115                    "\t--no-fullscreen                           "+tr("Don't launch in fullscreen mode")+"\n"+
     116                    "\t--reset-preferences                       "+tr("Reset the preferences to default")+"\n\n"+
     117                    "\t--language=<language>                     "+tr("Set the language.")+"\n\n"+
     118                    tr("examples")+":\n"+
     119                    "\tjava -jar josm.jar track1.gpx track2.gpx london.osm\n"+
     120                    "\tjava -jar josm.jar http://www.openstreetmap.org/index.html?lat=43.2&lon=11.1&zoom=13\n"+
     121                    "\tjava -jar josm.jar london.osm --selection=http://www.ostertag.name/osm/OSM_errors_node-duplicate.xml\n"+
     122                    "\tjava -jar josm.jar 43.2,11.1,43.4,11.4\n\n"+
     123
     124                    tr("Parameters are read in the order they are specified, so make sure you load\n"+
     125                    "some data before --selection")+"\n\n"+
     126                    tr("Instead of --download=<bbox> you may specify osm://<bbox>\n"));
     127            System.exit(0);
     128        }
     129
     130        // get the preferences.
     131        final File prefDir = new File(Main.pref.getPreferencesDir());
     132        // check if preferences directory has moved (TODO: Update code. Remove this after some time)
     133        File oldPrefDir = new File(System.getProperty("user.home"), ".josm");
     134        if (!prefDir.isDirectory() && oldPrefDir.isDirectory()) {
     135            if (oldPrefDir.renameTo(prefDir)) {
     136                // do not translate this
     137                JOptionPane.showMessageDialog(null, "The preference directory has been moved to "+prefDir);
     138            } else {
     139                JOptionPane.showMessageDialog(null, "The preference directory location has changed. Please move "+oldPrefDir+" to "+prefDir);
     140            }
     141        }
     142
     143        if (prefDir.exists() && !prefDir.isDirectory()) {
     144            JOptionPane.showMessageDialog(null, "Cannot open preferences directory: "+Main.pref.getPreferencesDir());
     145            return;
     146        }
     147        if (!prefDir.exists())
     148            prefDir.mkdirs();
     149
     150        if (!new File(Main.pref.getPreferencesDir()+"preferences").exists()) {
     151            Main.pref.resetToDefault();
     152        }
     153
     154        try {
     155            if (args.containsKey("reset-preferences")) {
     156                Main.pref.resetToDefault();
     157            } else {
     158                Main.pref.load();
     159            }
     160        } catch (final IOException e1) {
     161            e1.printStackTrace();
     162            String backup = Main.pref.getPreferencesDir() + "preferences.bak";
     163            JOptionPane.showMessageDialog(null, "Preferences file had errors.  Making backup of old one to " + backup);
     164            new File(Main.pref.getPreferencesDir() + "preferences").renameTo(new File(backup));
     165            Main.pref.save();
     166        }
    167167
    168168        // TODO remove this in early 2009 - just here to weed out color setting we don't use any more
    169169        Main.pref.put("downloaded Area", null);
    170170
    171                 String localeName = null; //The locale to use
    172        
    173                 //Check if passed as parameter
    174                 if(args.containsKey("language"))
    175                     localeName = (String)(args.get("language").toArray()[0]);
    176                
    177                 if (localeName == null) {
    178                         localeName = Main.pref.get("language", null);
    179                 }
    180 
    181                 if (localeName != null) {
    182                         Locale l;
    183                         int i = localeName.indexOf('_');
    184                         if (i > 0) {
    185                                 l = new Locale(localeName.substring(0, i), localeName.substring(i + 1));
    186                         } else {
    187                                 l = new Locale(localeName);
    188                         }
    189                         Locale.setDefault(l);
    190                 }
     171        String localeName = null; //The locale to use
     172
     173        //Check if passed as parameter
     174        if(args.containsKey("language"))
     175            localeName = (String)(args.get("language").toArray()[0]);
     176
     177        if (localeName == null) {
     178            localeName = Main.pref.get("language", null);
     179        }
     180
     181        if (localeName != null) {
     182            Locale l;
     183            int i = localeName.indexOf('_');
     184            if (i > 0) {
     185                l = new Locale(localeName.substring(0, i), localeName.substring(i + 1));
     186            } else {
     187                l = new Locale(localeName);
     188            }
     189            Locale.setDefault(l);
     190        }
    191191        try {
    192192            i18n = I18nFactory.getI18n(MainApplication.class);
     
    194194            System.out.println("Unable to find translation for the locale: " + Locale.getDefault().getDisplayName() + " reverting to English.");
    195195        }
    196                
    197                 SplashScreen splash = new SplashScreen(Main.pref.getBoolean("draw.splashscreen", true));
    198 
    199                 splash.setStatus(tr("Activating updated plugins"));
    200                 if (!PluginDownloader.moveUpdatedPlugins()) {
    201                         JOptionPane.showMessageDialog(null,
    202                                 tr("Activating the updated plugins failed. Check if JOSM has the permission to overwrite the existing ones."),
    203                                 tr("Plugins"), JOptionPane.ERROR_MESSAGE);
    204                 }
    205 
    206                 // load the early plugins
    207                 splash.setStatus(tr("Loading early plugins"));
    208                 Main.loadPlugins(true);
    209 
    210                 splash.setStatus(tr("Setting defaults"));
    211                 preConstructorInit(args);
    212                 splash.setStatus(tr("Creating main GUI"));
    213                 JFrame mainFrame = new JFrame(tr("Java OpenStreetMap - Editor"));
    214                 Main.parent = mainFrame;
    215                 final Main main = new MainApplication(mainFrame);
    216                 splash.setStatus(tr("Loading plugins"));
    217                 Main.loadPlugins(false);
    218                 toolbar.refreshToolbarControl();
    219 
    220                 mainFrame.setVisible(true);
    221                 splash.closeSplash();
    222 
    223                 if (!args.containsKey("no-fullscreen") && !args.containsKey("geometry") && Toolkit.getDefaultToolkit().isFrameStateSupported(JFrame.MAXIMIZED_BOTH))
    224                         mainFrame.setExtendedState(JFrame.MAXIMIZED_BOTH);
    225 
    226                 EventQueue.invokeLater(new Runnable(){
    227                         public void run() {
    228                                 main.postConstructorProcessCmdLine(args);
    229                         }
    230                 });
    231         }
     196
     197        SplashScreen splash = new SplashScreen(Main.pref.getBoolean("draw.splashscreen", true));
     198
     199        splash.setStatus(tr("Activating updated plugins"));
     200        if (!PluginDownloader.moveUpdatedPlugins()) {
     201            JOptionPane.showMessageDialog(null,
     202                    tr("Activating the updated plugins failed. Check if JOSM has the permission to overwrite the existing ones."),
     203                    tr("Plugins"), JOptionPane.ERROR_MESSAGE);
     204        }
     205
     206        // load the early plugins
     207        splash.setStatus(tr("Loading early plugins"));
     208        Main.loadPlugins(true);
     209
     210        splash.setStatus(tr("Setting defaults"));
     211        preConstructorInit(args);
     212        splash.setStatus(tr("Creating main GUI"));
     213        JFrame mainFrame = new JFrame(tr("Java OpenStreetMap - Editor"));
     214        Main.parent = mainFrame;
     215        final Main main = new MainApplication(mainFrame);
     216        splash.setStatus(tr("Loading plugins"));
     217        Main.loadPlugins(false);
     218        toolbar.refreshToolbarControl();
     219
     220        mainFrame.setVisible(true);
     221        splash.closeSplash();
     222
     223        if (!args.containsKey("no-fullscreen") && !args.containsKey("geometry") && Toolkit.getDefaultToolkit().isFrameStateSupported(JFrame.MAXIMIZED_BOTH))
     224            mainFrame.setExtendedState(JFrame.MAXIMIZED_BOTH);
     225
     226        EventQueue.invokeLater(new Runnable(){
     227            public void run() {
     228                main.postConstructorProcessCmdLine(args);
     229            }
     230        });
     231    }
    232232
    233233}
  • trunk/src/org/openstreetmap/josm/gui/MainMenu.java

    r1161 r1169  
    6868 * This is the JOSM main menu bar. It is overwritten to initialize itself and provide all menu
    6969 * entries as member variables (sort of collect them).
    70  * 
     70 *
    7171 * It also provides possibilities to attach new menu entries (used by plugins).
    72  * 
     72 *
    7373 * @author Immanuel.Scholz
    7474 */
     
    140140    /**
    141141     * Add a JosmAction to a menu.
    142      * 
     142     *
    143143     * This method handles all the shortcut handling. It also makes sure that actions that are
    144144     * handled by the OS are not duplicated on the menu.
     
    156156    /**
    157157     * Add a menu to the main menu.
    158      * 
     158     *
    159159     * This method handles all the shortcut handling.
    160160     */
  • trunk/src/org/openstreetmap/josm/gui/MapFrame.java

    r1153 r1169  
    3535 * One Map frame with one dataset behind. This is the container gui class whose
    3636 * display can be set to the different views.
    37  * 
     37 *
    3838 * @author imi
    3939 */
    4040public class MapFrame extends JPanel implements Destroyable {
    4141
    42         /**
    43         * The current mode, this frame operates.
    44         */
    45         public MapMode mapMode;
    46         /**
    47         * The view control displayed.
    48         */
    49         public MapView mapView;
    50         /**
    51         * The toolbar with the action icons. To add new toggle dialog actions, use addToggleDialog
    52         * instead of adding directly to this list. To add a new mode use addMapMode.
    53         */
    54         public JToolBar toolBarActions = new JToolBar(JToolBar.VERTICAL);
    55         public JToolBar toolBarToggle = new JToolBar(JToolBar.VERTICAL);
    56         /**
    57         * The status line below the map
    58         */
    59         public MapStatus statusLine;
    60 
    61         public ConflictDialog conflictDialog;
    62         /**
    63         * The dialog that shows all relations and lets the user edit them.
    64         */
    65         public RelationListDialog relationListDialog;
    66         /**
    67         * The panel list of all toggle dialog icons. To add new toggle dialog actions, use addToggleDialog
    68         * instead of adding directly to this list.
    69         */
    70         public JPanel toggleDialogs = new JPanel();
    71 
    72         public final ButtonGroup toolGroup = new ButtonGroup();
    73 
    74         public MapFrame() {
    75                 setSize(400,400);
    76                 setLayout(new BorderLayout());
    77 
    78                 add(mapView = new MapView(), BorderLayout.CENTER);
    79 
    80                 // show menu entry
    81                 Main.main.menu.viewMenu.setVisible(true);
    82 
    83                 // toolbar
    84                 toolBarActions.setFloatable(false);
    85                 addMapMode(new IconToggleButton(new ZoomAction(this)));
    86                 addMapMode(new IconToggleButton(new SelectAction(this)));
    87                 addMapMode(new IconToggleButton(new DrawAction(this)));
    88                 addMapMode(new IconToggleButton(new DeleteAction(this)));
    89                 addMapMode(new IconToggleButton(new ExtrudeAction(this)));
    90 
    91                 toolGroup.setSelected(((AbstractButton)toolBarActions.getComponent(0)).getModel(), true);
    92 
    93                 add(toggleDialogs, BorderLayout.EAST);
    94                 toggleDialogs.setLayout(new BoxLayout(toggleDialogs, BoxLayout.Y_AXIS));
    95 
    96                 toolBarToggle.setFloatable(false);
    97                 addToggleDialog(new LayerListDialog(this));
    98                 addToggleDialog(new PropertiesDialog(this));
    99                 addToggleDialog(new HistoryDialog());
    100                 addToggleDialog(new SelectionListDialog());
    101                 addToggleDialog(new UserListDialog());
    102                 addToggleDialog(conflictDialog = new ConflictDialog());
    103                 addToggleDialog(new CommandStackDialog(this));
    104                 addToggleDialog(relationListDialog = new RelationListDialog());
    105 
    106                 // status line below the map
    107                 statusLine = new MapStatus(this);
    108         }
    109 
    110         /**
    111         * Called as some kind of destructor when the last layer has been removed.
    112         * Delegates the call to all Destroyables within this component (e.g. MapModes)
    113         */
    114         public void destroy() {
    115                 for (int i = 0; i < toolBarActions.getComponentCount(); ++i)
    116                         if (toolBarActions.getComponent(i) instanceof Destroyable)
    117                                 ((Destroyable)toolBarActions).destroy();
    118                 for (int i = 0; i < toolBarToggle.getComponentCount(); ++i)
    119                         if (toolBarToggle.getComponent(i) instanceof Destroyable)
    120                                 ((Destroyable)toolBarToggle).destroy();
    121                
    122                 // remove menu entries
    123                 Main.main.menu.viewMenu.setVisible(false);
    124     }
    125 
    126         public Action getDefaultButtonAction() {
    127                 return ((AbstractButton)toolBarActions.getComponent(0)).getAction();
    128         }
    129 
    130         /**
    131         * Open all ToggleDialogs that have their preferences property set. Close all others.
    132         */
    133         public void setVisibleDialogs() {
    134                 for (Component c : toggleDialogs.getComponents()) {
    135                         if (c instanceof ToggleDialog) {
    136                                 boolean sel = Main.pref.getBoolean(((ToggleDialog)c).prefName+".visible");
    137                                 ((ToggleDialog)c).action.button.setSelected(sel);
    138                                 c.setVisible(sel);
    139                         }
    140                 }
    141         }
    142 
    143         /**
    144         * Call this to add new toggle dialogs to the left button-list
    145         * @param dlg The toggle dialog. It must not be in the list already.
    146         */
    147         public void addToggleDialog(ToggleDialog dlg) {
    148                 IconToggleButton button = new IconToggleButton(dlg.action);
    149                 dlg.action.button = button;
    150                 dlg.parent = toggleDialogs;
    151                 toolBarToggle.add(button);
    152                 toggleDialogs.add(dlg);
    153         }
    154 
    155         public void addMapMode(IconToggleButton b) {
    156                 toolBarActions.add(b);
    157                 toolGroup.add((AbstractButton)b);
    158         }
    159 
    160         /**
    161         * Fires an property changed event "visible".
    162         */
    163         @Override public void setVisible(boolean aFlag) {
    164                 boolean old = isVisible();
    165                 super.setVisible(aFlag);
    166                 if (old != aFlag)
    167                         firePropertyChange("visible", old, aFlag);
    168         }
    169 
    170 
    171 
    172         /**
    173         * Change the operating map mode for the view. Will call unregister on the
    174         * old MapMode and register on the new one.
    175          * @param mapMode       The new mode to set.
    176         */
    177         public void selectMapMode(MapMode mapMode) {
    178                 if (mapMode == this.mapMode)
    179                         return;
    180                 if (this.mapMode != null)
    181                         this.mapMode.exitMode();
    182                 this.mapMode = mapMode;
    183                 mapMode.enterMode();
    184         }
    185 
    186         /**
    187         * Fill the given panel by adding all necessary components to the different
    188         * locations.
    189          *
    190         * @param panel The container to fill. Must have an BorderLayout.
    191         */
    192         public void fillPanel(Container panel) {
    193                 panel.add(this, BorderLayout.CENTER);
    194                 JToolBar jb = new JToolBar(JToolBar.VERTICAL);
    195                 jb.setFloatable(false);
    196                 jb.add(toolBarActions);
    197                 jb.addSeparator();
    198                 jb.add(toolBarToggle);
    199                 panel.add(jb, BorderLayout.WEST);
    200                 if (statusLine != null)
    201                         panel.add(statusLine, BorderLayout.SOUTH);
    202         }
     42    /**
     43    * The current mode, this frame operates.
     44    */
     45    public MapMode mapMode;
     46    /**
     47    * The view control displayed.
     48    */
     49    public MapView mapView;
     50    /**
     51    * The toolbar with the action icons. To add new toggle dialog actions, use addToggleDialog
     52    * instead of adding directly to this list. To add a new mode use addMapMode.
     53    */
     54    public JToolBar toolBarActions = new JToolBar(JToolBar.VERTICAL);
     55    public JToolBar toolBarToggle = new JToolBar(JToolBar.VERTICAL);
     56    /**
     57    * The status line below the map
     58    */
     59    public MapStatus statusLine;
     60
     61    public ConflictDialog conflictDialog;
     62    /**
     63    * The dialog that shows all relations and lets the user edit them.
     64    */
     65    public RelationListDialog relationListDialog;
     66    /**
     67    * The panel list of all toggle dialog icons. To add new toggle dialog actions, use addToggleDialog
     68    * instead of adding directly to this list.
     69    */
     70    public JPanel toggleDialogs = new JPanel();
     71
     72    public final ButtonGroup toolGroup = new ButtonGroup();
     73
     74    public MapFrame() {
     75        setSize(400,400);
     76        setLayout(new BorderLayout());
     77
     78        add(mapView = new MapView(), BorderLayout.CENTER);
     79
     80        // show menu entry
     81        Main.main.menu.viewMenu.setVisible(true);
     82
     83        // toolbar
     84        toolBarActions.setFloatable(false);
     85        addMapMode(new IconToggleButton(new ZoomAction(this)));
     86        addMapMode(new IconToggleButton(new SelectAction(this)));
     87        addMapMode(new IconToggleButton(new DrawAction(this)));
     88        addMapMode(new IconToggleButton(new DeleteAction(this)));
     89        addMapMode(new IconToggleButton(new ExtrudeAction(this)));
     90
     91        toolGroup.setSelected(((AbstractButton)toolBarActions.getComponent(0)).getModel(), true);
     92
     93        add(toggleDialogs, BorderLayout.EAST);
     94        toggleDialogs.setLayout(new BoxLayout(toggleDialogs, BoxLayout.Y_AXIS));
     95
     96        toolBarToggle.setFloatable(false);
     97        addToggleDialog(new LayerListDialog(this));
     98        addToggleDialog(new PropertiesDialog(this));
     99        addToggleDialog(new HistoryDialog());
     100        addToggleDialog(new SelectionListDialog());
     101        addToggleDialog(new UserListDialog());
     102        addToggleDialog(conflictDialog = new ConflictDialog());
     103        addToggleDialog(new CommandStackDialog(this));
     104        addToggleDialog(relationListDialog = new RelationListDialog());
     105
     106        // status line below the map
     107        statusLine = new MapStatus(this);
     108    }
     109
     110    /**
     111    * Called as some kind of destructor when the last layer has been removed.
     112    * Delegates the call to all Destroyables within this component (e.g. MapModes)
     113    */
     114    public void destroy() {
     115        for (int i = 0; i < toolBarActions.getComponentCount(); ++i)
     116            if (toolBarActions.getComponent(i) instanceof Destroyable)
     117                ((Destroyable)toolBarActions).destroy();
     118        for (int i = 0; i < toolBarToggle.getComponentCount(); ++i)
     119            if (toolBarToggle.getComponent(i) instanceof Destroyable)
     120                ((Destroyable)toolBarToggle).destroy();
     121
     122        // remove menu entries
     123        Main.main.menu.viewMenu.setVisible(false);
     124    }
     125
     126    public Action getDefaultButtonAction() {
     127        return ((AbstractButton)toolBarActions.getComponent(0)).getAction();
     128    }
     129
     130    /**
     131    * Open all ToggleDialogs that have their preferences property set. Close all others.
     132    */
     133    public void setVisibleDialogs() {
     134        for (Component c : toggleDialogs.getComponents()) {
     135            if (c instanceof ToggleDialog) {
     136                boolean sel = Main.pref.getBoolean(((ToggleDialog)c).prefName+".visible");
     137                ((ToggleDialog)c).action.button.setSelected(sel);
     138                c.setVisible(sel);
     139            }
     140        }
     141    }
     142
     143    /**
     144    * Call this to add new toggle dialogs to the left button-list
     145    * @param dlg The toggle dialog. It must not be in the list already.
     146    */
     147    public void addToggleDialog(ToggleDialog dlg) {
     148        IconToggleButton button = new IconToggleButton(dlg.action);
     149        dlg.action.button = button;
     150        dlg.parent = toggleDialogs;
     151        toolBarToggle.add(button);
     152        toggleDialogs.add(dlg);
     153    }
     154
     155    public void addMapMode(IconToggleButton b) {
     156        toolBarActions.add(b);
     157        toolGroup.add((AbstractButton)b);
     158    }
     159
     160    /**
     161    * Fires an property changed event "visible".
     162    */
     163    @Override public void setVisible(boolean aFlag) {
     164        boolean old = isVisible();
     165        super.setVisible(aFlag);
     166        if (old != aFlag)
     167            firePropertyChange("visible", old, aFlag);
     168    }
     169
     170
     171
     172    /**
     173    * Change the operating map mode for the view. Will call unregister on the
     174    * old MapMode and register on the new one.
     175     * @param mapMode   The new mode to set.
     176    */
     177    public void selectMapMode(MapMode mapMode) {
     178        if (mapMode == this.mapMode)
     179            return;
     180        if (this.mapMode != null)
     181            this.mapMode.exitMode();
     182        this.mapMode = mapMode;
     183        mapMode.enterMode();
     184    }
     185
     186    /**
     187    * Fill the given panel by adding all necessary components to the different
     188    * locations.
     189     *
     190    * @param panel The container to fill. Must have an BorderLayout.
     191    */
     192    public void fillPanel(Container panel) {
     193        panel.add(this, BorderLayout.CENTER);
     194        JToolBar jb = new JToolBar(JToolBar.VERTICAL);
     195        jb.setFloatable(false);
     196        jb.add(toolBarActions);
     197        jb.addSeparator();
     198        jb.add(toolBarToggle);
     199        panel.add(jb, BorderLayout.WEST);
     200        if (statusLine != null)
     201            panel.add(statusLine, BorderLayout.SOUTH);
     202    }
    203203}
  • trunk/src/org/openstreetmap/josm/gui/MapMover.java

    r1084 r1169  
    2828public class MapMover extends MouseAdapter implements MouseMotionListener, MouseWheelListener {
    2929
    30         private final class ZoomerAction extends AbstractAction {
    31                 private final String action;
    32                 public ZoomerAction(String action) {
    33                         this.action = action;
     30    private final class ZoomerAction extends AbstractAction {
     31        private final String action;
     32        public ZoomerAction(String action) {
     33            this.action = action;
    3434        }
    35             public void actionPerformed(ActionEvent e) {
    36                 if (action.equals(".") || action.equals(",")) {
    37                         Point mouse = nc.getMousePosition();
    38                         if (mouse == null)
    39                                 mouse = new Point((int)nc.getBounds().getCenterX(), (int)nc.getBounds().getCenterY());
    40                         MouseWheelEvent we = new MouseWheelEvent(nc, e.getID(), e.getWhen(), e.getModifiers(), mouse.x, mouse.y, 0, false, MouseWheelEvent.WHEEL_UNIT_SCROLL, 1, action.equals(",") ? -1 : 1);
    41                         mouseWheelMoved(we);
    42                 } else {
    43                         EastNorth center = nc.getCenter();
    44                         EastNorth newcenter = nc.getEastNorth(nc.getWidth()/2+nc.getWidth()/5, nc.getHeight()/2+nc.getHeight()/5);
    45                         if (action.equals("left"))
    46                                 nc.zoomTo(new EastNorth(2*center.east()-newcenter.east(), center.north()), nc.getScale());
    47                         else if (action.equals("right"))
    48                                 nc.zoomTo(new EastNorth(newcenter.east(), center.north()), nc.getScale());
    49                         else if (action.equals("up"))
    50                                 nc.zoomTo(new EastNorth(center.east(), 2*center.north()-newcenter.north()), nc.getScale());
    51                         else if (action.equals("down"))
    52                                 nc.zoomTo(new EastNorth(center.east(), newcenter.north()), nc.getScale());
    53                 }
    54             }
    55     }
    56 
    57         /**
    58         * The point in the map that was the under the mouse point
    59         * when moving around started.
    60         */
    61         private EastNorth mousePosMove;
    62         /**
    63         * The map to move around.
    64         */
    65         private final NavigatableComponent nc;
    66         /**
    67         * The old cursor when we changed it to movement cursor.
    68         */
    69         private Cursor oldCursor;
    70 
    71         private boolean movementInPlace = false;
    72 
    73         /**
    74         * Create a new MapMover
    75         */
    76         public MapMover(NavigatableComponent navComp, JPanel contentPane) {
    77                 this.nc = navComp;
    78                 nc.addMouseListener(this);
    79                 nc.addMouseMotionListener(this);
    80                 nc.addMouseWheelListener(this);
    81 
    82                 if (contentPane != null) {
    83                         contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
    84                                 Shortcut.registerShortcut("system:movefocusright", tr("Map: {0}", tr("Move right")), KeyEvent.VK_RIGHT, Shortcut.GROUP_HOTKEY).getKeyStroke(),
    85                                 "MapMover.Zoomer.right");
    86                         contentPane.getActionMap().put("MapMover.Zoomer.right", new ZoomerAction("right"));
    87 
    88                         contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
    89                                 Shortcut.registerShortcut("system:movefocusleft", tr("Map: {0}", tr("Move left")), KeyEvent.VK_LEFT, Shortcut.GROUP_HOTKEY).getKeyStroke(),
    90                                 "MapMover.Zoomer.left");
    91                         contentPane.getActionMap().put("MapMover.Zoomer.left", new ZoomerAction("left"));
    92 
    93                         contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
    94                                 Shortcut.registerShortcut("system:movefocusup", tr("Map: {0}", tr("Move up")), KeyEvent.VK_UP, Shortcut.GROUP_HOTKEY).getKeyStroke(),
    95                                 "MapMover.Zoomer.up");
    96                         contentPane.getActionMap().put("MapMover.Zoomer.up", new ZoomerAction("up"));
    97 
    98                         contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
    99                                 Shortcut.registerShortcut("system:movefocusdown", tr("Map: {0}", tr("Move down")), KeyEvent.VK_DOWN, Shortcut.GROUP_HOTKEY).getKeyStroke(),
    100                                 "MapMover.Zoomer.down");
    101                         contentPane.getActionMap().put("MapMover.Zoomer.down", new ZoomerAction("down"));
    102 
    103                         contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
    104                                 Shortcut.registerShortcut("view:zoominalternate", tr("Map: {0}", tr("Zoom in")), KeyEvent.VK_COMMA, Shortcut.GROUP_HOTKEY).getKeyStroke(),
    105                                 "MapMover.Zoomer.in");
    106                         contentPane.getActionMap().put("MapMover.Zoomer.in", new ZoomerAction(","));
    107 
    108                         contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
    109                                 Shortcut.registerShortcut("view:zoomoutalternate", tr("Map: {0}", tr("Zoom out")), KeyEvent.VK_PERIOD, Shortcut.GROUP_HOTKEY).getKeyStroke(),
    110                                 "MapMover.Zoomer.out");
    111                         contentPane.getActionMap().put("MapMover.Zoomer.out", new ZoomerAction("."));
    112                 }
    113         }
    114 
    115         /**
    116         * If the right (and only the right) mouse button is pressed, move the map
    117         */
    118         public void mouseDragged(MouseEvent e) {
    119                 int offMask = MouseEvent.BUTTON1_DOWN_MASK | MouseEvent.BUTTON2_DOWN_MASK;
    120                 if ((e.getModifiersEx() & (MouseEvent.BUTTON3_DOWN_MASK | offMask)) == MouseEvent.BUTTON3_DOWN_MASK) {
    121                         if (mousePosMove == null)
    122                                 startMovement(e);
    123                         EastNorth center = nc.getCenter();
    124                         EastNorth mouseCenter = nc.getEastNorth(e.getX(), e.getY());
    125                         EastNorth p = new EastNorth(
    126                                         mousePosMove.east() + center.east() - mouseCenter.east(),
    127                                         mousePosMove.north() + center.north() - mouseCenter.north());
    128                         nc.zoomTo(p, nc.getScale());
    129                 } else
    130                         endMovement();
    131         }
    132 
    133         /**
    134         * Start the movement, if it was the 3rd button (right button).
    135         */
    136         @Override public void mousePressed(MouseEvent e) {
    137                 int offMask = MouseEvent.BUTTON1_DOWN_MASK | MouseEvent.BUTTON2_DOWN_MASK;
    138                 if (e.getButton() == MouseEvent.BUTTON3 && (e.getModifiersEx() & offMask) == 0)
    139                         startMovement(e);
    140         }
    141 
    142         /**
    143         * Change the cursor back to it's pre-move cursor.
    144         */
    145         @Override public void mouseReleased(MouseEvent e) {
    146                 if (e.getButton() == MouseEvent.BUTTON3)
    147                         endMovement();
    148         }
    149 
    150         /**
    151         * Start movement by setting a new cursor and remember the current mouse
    152         * position.
    153         * @param e The mouse event that leat to the movement from.
    154         */
    155         private void startMovement(MouseEvent e) {
    156                 if (movementInPlace)
    157                         return;
    158                 movementInPlace = true;
    159                 mousePosMove = nc.getEastNorth(e.getX(), e.getY());
    160                 oldCursor = nc.getCursor();
    161                 nc.setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));
    162         }
    163 
    164         /**
    165         * End the movement. Setting back the cursor and clear the movement variables
    166         */
    167         private void endMovement() {
    168                 if (!movementInPlace)
    169                         return;
    170                 movementInPlace = false;
    171                 if (oldCursor != null)
    172                         nc.setCursor(oldCursor);
    173                 else
    174                         nc.setCursor(Cursor.getDefaultCursor());
    175                 mousePosMove = null;
    176                 oldCursor = null;
    177         }
    178 
    179         /**
    180         * Zoom the map by 1/5th of current zoom per wheel-delta.
    181         * @param e The wheel event.
    182         */
    183         public void mouseWheelMoved(MouseWheelEvent e) {
    184                 int w = nc.getWidth();
    185                 int h = nc.getHeight();
    186 
    187                 double zoom = Math.max(0.1, 1 + e.getWheelRotation()/5.0);
    188                 double zoomfactor = (zoom-1)/2+1;
    189 
    190                 double newHalfWidth = w*zoomfactor - w/2;
    191                 double newHalfHeight = h*zoomfactor - h/2;
    192                 double centerx = e.getX() - (e.getX()-w/2)*newHalfWidth*2/w;
    193                 double centery = e.getY() - (e.getY()-h/2)*newHalfHeight*2/h;
    194                 EastNorth newCenter = nc.getEastNorth((int)centerx, (int)centery);
    195 
    196                 nc.zoomTo(newCenter, nc.getScale()*zoom);
    197         }
    198 
    199         /**
    200         * Does nothing. Only to satisfy MouseMotionListener
    201         */
    202         public void mouseMoved(MouseEvent e) {}
     35        public void actionPerformed(ActionEvent e) {
     36            if (action.equals(".") || action.equals(",")) {
     37                Point mouse = nc.getMousePosition();
     38                if (mouse == null)
     39                    mouse = new Point((int)nc.getBounds().getCenterX(), (int)nc.getBounds().getCenterY());
     40                MouseWheelEvent we = new MouseWheelEvent(nc, e.getID(), e.getWhen(), e.getModifiers(), mouse.x, mouse.y, 0, false, MouseWheelEvent.WHEEL_UNIT_SCROLL, 1, action.equals(",") ? -1 : 1);
     41                mouseWheelMoved(we);
     42            } else {
     43                EastNorth center = nc.getCenter();
     44                EastNorth newcenter = nc.getEastNorth(nc.getWidth()/2+nc.getWidth()/5, nc.getHeight()/2+nc.getHeight()/5);
     45                if (action.equals("left"))
     46                    nc.zoomTo(new EastNorth(2*center.east()-newcenter.east(), center.north()), nc.getScale());
     47                else if (action.equals("right"))
     48                    nc.zoomTo(new EastNorth(newcenter.east(), center.north()), nc.getScale());
     49                else if (action.equals("up"))
     50                    nc.zoomTo(new EastNorth(center.east(), 2*center.north()-newcenter.north()), nc.getScale());
     51                else if (action.equals("down"))
     52                    nc.zoomTo(new EastNorth(center.east(), newcenter.north()), nc.getScale());
     53            }
     54        }
     55    }
     56
     57    /**
     58    * The point in the map that was the under the mouse point
     59    * when moving around started.
     60    */
     61    private EastNorth mousePosMove;
     62    /**
     63    * The map to move around.
     64    */
     65    private final NavigatableComponent nc;
     66    /**
     67    * The old cursor when we changed it to movement cursor.
     68    */
     69    private Cursor oldCursor;
     70
     71    private boolean movementInPlace = false;
     72
     73    /**
     74    * Create a new MapMover
     75    */
     76    public MapMover(NavigatableComponent navComp, JPanel contentPane) {
     77        this.nc = navComp;
     78        nc.addMouseListener(this);
     79        nc.addMouseMotionListener(this);
     80        nc.addMouseWheelListener(this);
     81
     82        if (contentPane != null) {
     83            contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
     84                Shortcut.registerShortcut("system:movefocusright", tr("Map: {0}", tr("Move right")), KeyEvent.VK_RIGHT, Shortcut.GROUP_HOTKEY).getKeyStroke(),
     85                "MapMover.Zoomer.right");
     86            contentPane.getActionMap().put("MapMover.Zoomer.right", new ZoomerAction("right"));
     87
     88            contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
     89                Shortcut.registerShortcut("system:movefocusleft", tr("Map: {0}", tr("Move left")), KeyEvent.VK_LEFT, Shortcut.GROUP_HOTKEY).getKeyStroke(),
     90                "MapMover.Zoomer.left");
     91            contentPane.getActionMap().put("MapMover.Zoomer.left", new ZoomerAction("left"));
     92
     93            contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
     94                Shortcut.registerShortcut("system:movefocusup", tr("Map: {0}", tr("Move up")), KeyEvent.VK_UP, Shortcut.GROUP_HOTKEY).getKeyStroke(),
     95                "MapMover.Zoomer.up");
     96            contentPane.getActionMap().put("MapMover.Zoomer.up", new ZoomerAction("up"));
     97
     98            contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
     99                Shortcut.registerShortcut("system:movefocusdown", tr("Map: {0}", tr("Move down")), KeyEvent.VK_DOWN, Shortcut.GROUP_HOTKEY).getKeyStroke(),
     100                "MapMover.Zoomer.down");
     101            contentPane.getActionMap().put("MapMover.Zoomer.down", new ZoomerAction("down"));
     102
     103            contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
     104                Shortcut.registerShortcut("view:zoominalternate", tr("Map: {0}", tr("Zoom in")), KeyEvent.VK_COMMA, Shortcut.GROUP_HOTKEY).getKeyStroke(),
     105                "MapMover.Zoomer.in");
     106            contentPane.getActionMap().put("MapMover.Zoomer.in", new ZoomerAction(","));
     107
     108            contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
     109                Shortcut.registerShortcut("view:zoomoutalternate", tr("Map: {0}", tr("Zoom out")), KeyEvent.VK_PERIOD, Shortcut.GROUP_HOTKEY).getKeyStroke(),
     110                "MapMover.Zoomer.out");
     111            contentPane.getActionMap().put("MapMover.Zoomer.out", new ZoomerAction("."));
     112        }
     113    }
     114
     115    /**
     116    * If the right (and only the right) mouse button is pressed, move the map
     117    */
     118    public void mouseDragged(MouseEvent e) {
     119        int offMask = MouseEvent.BUTTON1_DOWN_MASK | MouseEvent.BUTTON2_DOWN_MASK;
     120        if ((e.getModifiersEx() & (MouseEvent.BUTTON3_DOWN_MASK | offMask)) == MouseEvent.BUTTON3_DOWN_MASK) {
     121            if (mousePosMove == null)
     122                startMovement(e);
     123            EastNorth center = nc.getCenter();
     124            EastNorth mouseCenter = nc.getEastNorth(e.getX(), e.getY());
     125            EastNorth p = new EastNorth(
     126                    mousePosMove.east() + center.east() - mouseCenter.east(),
     127                    mousePosMove.north() + center.north() - mouseCenter.north());
     128            nc.zoomTo(p, nc.getScale());
     129        } else
     130            endMovement();
     131    }
     132
     133    /**
     134    * Start the movement, if it was the 3rd button (right button).
     135    */
     136    @Override public void mousePressed(MouseEvent e) {
     137        int offMask = MouseEvent.BUTTON1_DOWN_MASK | MouseEvent.BUTTON2_DOWN_MASK;
     138        if (e.getButton() == MouseEvent.BUTTON3 && (e.getModifiersEx() & offMask) == 0)
     139            startMovement(e);
     140    }
     141
     142    /**
     143    * Change the cursor back to it's pre-move cursor.
     144    */
     145    @Override public void mouseReleased(MouseEvent e) {
     146        if (e.getButton() == MouseEvent.BUTTON3)
     147            endMovement();
     148    }
     149
     150    /**
     151    * Start movement by setting a new cursor and remember the current mouse
     152    * position.
     153    * @param e The mouse event that leat to the movement from.
     154    */
     155    private void startMovement(MouseEvent e) {
     156        if (movementInPlace)
     157            return;
     158        movementInPlace = true;
     159        mousePosMove = nc.getEastNorth(e.getX(), e.getY());
     160        oldCursor = nc.getCursor();
     161        nc.setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));
     162    }
     163
     164    /**
     165    * End the movement. Setting back the cursor and clear the movement variables
     166    */
     167    private void endMovement() {
     168        if (!movementInPlace)
     169            return;
     170        movementInPlace = false;
     171        if (oldCursor != null)
     172            nc.setCursor(oldCursor);
     173        else
     174            nc.setCursor(Cursor.getDefaultCursor());
     175        mousePosMove = null;
     176        oldCursor = null;
     177    }
     178
     179    /**
     180    * Zoom the map by 1/5th of current zoom per wheel-delta.
     181    * @param e The wheel event.
     182    */
     183    public void mouseWheelMoved(MouseWheelEvent e) {
     184        int w = nc.getWidth();
     185        int h = nc.getHeight();
     186
     187        double zoom = Math.max(0.1, 1 + e.getWheelRotation()/5.0);
     188        double zoomfactor = (zoom-1)/2+1;
     189
     190        double newHalfWidth = w*zoomfactor - w/2;
     191        double newHalfHeight = h*zoomfactor - h/2;
     192        double centerx = e.getX() - (e.getX()-w/2)*newHalfWidth*2/w;
     193        double centery = e.getY() - (e.getY()-h/2)*newHalfHeight*2/h;
     194        EastNorth newCenter = nc.getEastNorth((int)centerx, (int)centery);
     195
     196        nc.zoomTo(newCenter, nc.getScale()*zoom);
     197    }
     198
     199    /**
     200    * Does nothing. Only to satisfy MouseMotionListener
     201    */
     202    public void mouseMoved(MouseEvent e) {}
    203203}
  • trunk/src/org/openstreetmap/josm/gui/MapScaler.java

    r999 r1169  
    1818public class MapScaler extends JComponent implements Helpful {
    1919
    20         private final NavigatableComponent mv;
    21         public MapScaler(NavigatableComponent mv, Projection proj) {
    22                 this.mv = mv;
    23                 setSize(100,30);
    24                 setOpaque(false);
     20    private final NavigatableComponent mv;
     21    public MapScaler(NavigatableComponent mv, Projection proj) {
     22        this.mv = mv;
     23        setSize(100,30);
     24        setOpaque(false);
    2525    }
    2626
    27         @Override public void paint(Graphics g) {
    28                 LatLon ll1 = mv.getLatLon(0,0);
    29                 LatLon ll2 = mv.getLatLon(100,0);
    30                 double dist = ll1.greatCircleDistance(ll2);
    31                 String text = dist > 1000 ? (Math.round(dist/100)/10.0)+" km" : Math.round(dist*10)/10+" m";
    32                 Rectangle2D bound = g.getFontMetrics().getStringBounds(text, g);
    33                 g.setColor(Main.pref.getColor(marktr("scale"), Color.white));
    34                 g.drawLine(0, 5, 99, 5);
    35                 g.drawLine(0, 0, 0, 10);
    36                 g.drawLine(99, 0, 99, 10);
    37                 g.drawLine(49, 0, 49, 10);
    38                 g.drawLine(24, 3, 24, 7);
    39                 g.drawLine(74, 3, 74, 7);
    40                 g.drawString(text, (int)(50-bound.getWidth()/2), 23);
     27    @Override public void paint(Graphics g) {
     28        LatLon ll1 = mv.getLatLon(0,0);
     29        LatLon ll2 = mv.getLatLon(100,0);
     30        double dist = ll1.greatCircleDistance(ll2);
     31        String text = dist > 1000 ? (Math.round(dist/100)/10.0)+" km" : Math.round(dist*10)/10+" m";
     32        Rectangle2D bound = g.getFontMetrics().getStringBounds(text, g);
     33        g.setColor(Main.pref.getColor(marktr("scale"), Color.white));
     34        g.drawLine(0, 5, 99, 5);
     35        g.drawLine(0, 0, 0, 10);
     36        g.drawLine(99, 0, 99, 10);
     37        g.drawLine(49, 0, 49, 10);
     38        g.drawLine(24, 3, 24, 7);
     39        g.drawLine(74, 3, 74, 7);
     40        g.drawString(text, (int)(50-bound.getWidth()/2), 23);
    4141    }
    4242
    43         public String helpTopic() {
    44             return "MapView/Scaler";
     43    public String helpTopic() {
     44        return "MapView/Scaler";
    4545    }
    4646}
  • trunk/src/org/openstreetmap/josm/gui/MapSlider.java

    r627 r1169  
    1515
    1616class MapSlider extends JSlider implements PropertyChangeListener, ChangeListener, Helpful {
    17        
     17
    1818    private final MapView mv;
    19         boolean clicked = false;
    20        
    21         public MapSlider(MapView mv) {
    22                 super(0, 20);
    23                 setOpaque(false);
    24                 this.mv = mv;
    25                 addMouseListener(new MouseAdapter(){
    26                         @Override public void mousePressed(MouseEvent e) {
    27                                 clicked = true;
    28                         }
    29                         @Override public void mouseReleased(MouseEvent e) {
    30                                 clicked = false;
    31                         }
    32                 });
    33                 mv.addPropertyChangeListener("scale", this);
    34                 addChangeListener(this);
    35         }
    36        
    37         public void propertyChange(PropertyChangeEvent evt) {
    38                 if (!getModel().getValueIsAdjusting())
    39                         setValue(this.mv.zoom());
    40         }
    41        
    42         public void stateChanged(ChangeEvent e) {
    43                 if (!clicked)
    44                         return;
    45                 EastNorth pos = MapView.world;
    46                 for (int zoom = 0; zoom < getValue(); ++zoom)
    47                         pos = new EastNorth(pos.east()/2, pos.north()/2);
    48                 if (this.mv.getWidth() < this.mv.getHeight())
    49                         this.mv.zoomTo(this.mv.center, pos.east()*2/(this.mv.getWidth()-20));
    50                 else
    51                         this.mv.zoomTo(this.mv.center, pos.north()*2/(this.mv.getHeight()-20));
    52         }
     19    boolean clicked = false;
    5320
    54         public String helpTopic() {
    55             return "MapView/Slider";
     21    public MapSlider(MapView mv) {
     22        super(0, 20);
     23        setOpaque(false);
     24        this.mv = mv;
     25        addMouseListener(new MouseAdapter(){
     26            @Override public void mousePressed(MouseEvent e) {
     27                clicked = true;
     28            }
     29            @Override public void mouseReleased(MouseEvent e) {
     30                clicked = false;
     31            }
     32        });
     33        mv.addPropertyChangeListener("scale", this);
     34        addChangeListener(this);
     35    }
     36
     37    public void propertyChange(PropertyChangeEvent evt) {
     38        if (!getModel().getValueIsAdjusting())
     39            setValue(this.mv.zoom());
     40    }
     41
     42    public void stateChanged(ChangeEvent e) {
     43        if (!clicked)
     44            return;
     45        EastNorth pos = MapView.world;
     46        for (int zoom = 0; zoom < getValue(); ++zoom)
     47            pos = new EastNorth(pos.east()/2, pos.north()/2);
     48        if (this.mv.getWidth() < this.mv.getHeight())
     49            this.mv.zoomTo(this.mv.center, pos.east()*2/(this.mv.getWidth()-20));
     50        else
     51            this.mv.zoomTo(this.mv.center, pos.north()*2/(this.mv.getHeight()-20));
     52    }
     53
     54    public String helpTopic() {
     55        return "MapView/Slider";
    5656    }
    5757}
  • trunk/src/org/openstreetmap/josm/gui/MapStatus.java

    r1108 r1169  
    5757public class MapStatus extends JPanel implements Helpful {
    5858
    59         /**
    60         * The MapView this status belongs to.
    61         */
    62         final MapView mv;
    63        
    64         /**
    65         * A small user interface component that consists of an image label and
    66         * a fixed text content to the right of the image.
    67         */
    68         class ImageLabel extends JPanel {
    69                 private JLabel tf;
    70                 private int chars;
    71                 public ImageLabel(String img, String tooltip, int chars) {
    72                         super();
    73                         setLayout(new GridBagLayout());
    74                         setBackground(Color.decode("#b8cfe5"));
    75                         add(new JLabel(ImageProvider.get("statusline/"+img+".png")), GBC.std().anchor(GBC.WEST).insets(0,1,1,0));
    76                         add(tf = new JLabel(), GBC.std().fill(GBC.BOTH).anchor(GBC.WEST).insets(2,1,1,0));
    77                         setToolTipText(tooltip);
    78                         this.chars = chars;
    79                 }
    80                 public void setText(String t) {
    81                         tf.setText(t);
    82                 }
    83                 @Override public Dimension getPreferredSize() {
    84                         return new Dimension(25 + chars*tf.getFontMetrics(tf.getFont()).charWidth('0'), super.getPreferredSize().height);
    85                 }
    86                 @Override public Dimension getMinimumSize() {
    87                         return new Dimension(25 + chars*tf.getFontMetrics(tf.getFont()).charWidth('0'), super.getMinimumSize().height);
    88                 }
    89         }
    90 
    91         LatLon.CoordinateFormat mCord;
     59    /**
     60    * The MapView this status belongs to.
     61    */
     62    final MapView mv;
     63
     64    /**
     65    * A small user interface component that consists of an image label and
     66    * a fixed text content to the right of the image.
     67    */
     68    class ImageLabel extends JPanel {
     69        private JLabel tf;
     70        private int chars;
     71        public ImageLabel(String img, String tooltip, int chars) {
     72            super();
     73            setLayout(new GridBagLayout());
     74            setBackground(Color.decode("#b8cfe5"));
     75            add(new JLabel(ImageProvider.get("statusline/"+img+".png")), GBC.std().anchor(GBC.WEST).insets(0,1,1,0));
     76            add(tf = new JLabel(), GBC.std().fill(GBC.BOTH).anchor(GBC.WEST).insets(2,1,1,0));
     77            setToolTipText(tooltip);
     78            this.chars = chars;
     79        }
     80        public void setText(String t) {
     81            tf.setText(t);
     82        }
     83        @Override public Dimension getPreferredSize() {
     84            return new Dimension(25 + chars*tf.getFontMetrics(tf.getFont()).charWidth('0'), super.getPreferredSize().height);
     85        }
     86        @Override public Dimension getMinimumSize() {
     87            return new Dimension(25 + chars*tf.getFontMetrics(tf.getFont()).charWidth('0'), super.getMinimumSize().height);
     88        }
     89    }
     90
     91    LatLon.CoordinateFormat mCord;
    9292
    9393    ImageLabel lonText = new ImageLabel("lon", tr("The geographic longitude at the mouse pointer."), 11);
     
    9999    ImageLabel distText = new ImageLabel("dist", tr("The length of the new way segment being drawn."), 8);
    100100
    101         /**
    102         * The collector class that waits for notification and then update
    103         * the display objects.
    104         *
    105         * @author imi
    106         */
    107         private final class Collector implements Runnable {
    108                 /**
    109                 * The last object displayed in status line.
    110                 */
    111                 Collection<OsmPrimitive> osmStatus;
    112                 /**
    113                 * The old modifiers that was pressed the last time this collector ran.
    114                 */
    115                 private int oldModifiers;
    116                 /**
    117                 * The popup displayed to show additional information
    118                 */
    119                 private Popup popup;
    120 
    121                 private MapFrame parent;
    122 
    123                 public Collector(MapFrame parent) {
    124                         this.parent = parent;
    125                 }
    126 
    127                 /**
    128                 * Execution function for the Collector.
    129                 */
    130                 public void run() {
    131                         for (;;) {
    132                                 MouseState ms = new MouseState();
    133                                 synchronized (this) {
    134                                         try {wait();} catch (InterruptedException e) {}
    135                                         ms.modifiers = mouseState.modifiers;
    136                                         ms.mousePos = mouseState.mousePos;
    137                                 }
    138                                 if (parent != Main.map)
    139                                         return; // exit, if new parent.
    140                                 if ((ms.modifiers & MouseEvent.CTRL_DOWN_MASK) != 0 || ms.mousePos == null)
    141                                         continue; // freeze display when holding down ctrl
    142 
    143                                 if (mv.center == null)
    144                                         continue;
    145 
    146                                 // This try/catch is a hack to stop the flooding bug reports about this.
    147                                 // The exception needed to handle with in the first place, means that this
    148                                 // access to the data need to be restarted, if the main thread modifies
    149                                 // the data.
    150                                 try {
    151                                         OsmPrimitive osmNearest = null;
    152                                         // Set the text label in the bottom status bar
    153                                         osmNearest = mv.getNearest(ms.mousePos);
    154                                         if (osmNearest != null) {
    155                                                 NameVisitor visitor = new NameVisitor();
    156                                                 osmNearest.visit(visitor);
    157                                                 nameText.setText(visitor.name);
    158                                         } else
    159                                                 nameText.setText(tr("(no object)"));
    160 
    161                                         // Popup Information
    162                                         if ((ms.modifiers & MouseEvent.BUTTON2_DOWN_MASK) != 0 ) {
    163                                                 Collection<OsmPrimitive> osms = mv.getAllNearest(ms.mousePos);
    164 
    165                                                 if (osms == null)
    166                                                         continue;
    167                                                 if (osms != null && osms.equals(osmStatus) && ms.modifiers == oldModifiers)
    168                                                         continue;
    169 
    170                                                 if (popup != null) {
    171                                                         try {
    172                                     EventQueue.invokeAndWait(new Runnable() {
    173                                         public void run() {
    174                                                 popup.hide();
    175                                         }
    176                                     });
     101    /**
     102    * The collector class that waits for notification and then update
     103    * the display objects.
     104    *
     105    * @author imi
     106    */
     107    private final class Collector implements Runnable {
     108        /**
     109        * The last object displayed in status line.
     110        */
     111        Collection<OsmPrimitive> osmStatus;
     112        /**
     113        * The old modifiers that was pressed the last time this collector ran.
     114        */
     115        private int oldModifiers;
     116        /**
     117        * The popup displayed to show additional information
     118        */
     119        private Popup popup;
     120
     121        private MapFrame parent;
     122
     123        public Collector(MapFrame parent) {
     124            this.parent = parent;
     125        }
     126
     127        /**
     128        * Execution function for the Collector.
     129        */
     130        public void run() {
     131            for (;;) {
     132                MouseState ms = new MouseState();
     133                synchronized (this) {
     134                    try {wait();} catch (InterruptedException e) {}
     135                    ms.modifiers = mouseState.modifiers;
     136                    ms.mousePos = mouseState.mousePos;
     137                }
     138                if (parent != Main.map)
     139                    return; // exit, if new parent.
     140                if ((ms.modifiers & MouseEvent.CTRL_DOWN_MASK) != 0 || ms.mousePos == null)
     141                    continue; // freeze display when holding down ctrl
     142
     143                if (mv.center == null)
     144                    continue;
     145
     146                // This try/catch is a hack to stop the flooding bug reports about this.
     147                // The exception needed to handle with in the first place, means that this
     148                // access to the data need to be restarted, if the main thread modifies
     149                // the data.
     150                try {
     151                    OsmPrimitive osmNearest = null;
     152                    // Set the text label in the bottom status bar
     153                    osmNearest = mv.getNearest(ms.mousePos);
     154                    if (osmNearest != null) {
     155                        NameVisitor visitor = new NameVisitor();
     156                        osmNearest.visit(visitor);
     157                        nameText.setText(visitor.name);
     158                    } else
     159                        nameText.setText(tr("(no object)"));
     160
     161                    // Popup Information
     162                    if ((ms.modifiers & MouseEvent.BUTTON2_DOWN_MASK) != 0 ) {
     163                        Collection<OsmPrimitive> osms = mv.getAllNearest(ms.mousePos);
     164
     165                        if (osms == null)
     166                            continue;
     167                        if (osms != null && osms.equals(osmStatus) && ms.modifiers == oldModifiers)
     168                            continue;
     169
     170                        if (popup != null) {
     171                            try {
     172                                EventQueue.invokeAndWait(new Runnable() {
     173                                    public void run() {
     174                                        popup.hide();
     175                                    }
     176                                });
    177177                            } catch (InterruptedException e) {
    178178                            } catch (InvocationTargetException e) {
    179                                 throw new RuntimeException(e);
     179                                throw new RuntimeException(e);
    180180                            }
    181                                                 }
    182 
    183                                                 JPanel c = new JPanel(new GridBagLayout());
    184                                                 for (final OsmPrimitive osm : osms) {
    185                                                         NameVisitor visitor = new NameVisitor();
    186                                                         osm.visit(visitor);
    187                                                         final StringBuilder text = new StringBuilder();
    188                                                         if (osm.id == 0 || osm.modified)
    189                                                                 visitor.name = "<i><b>"+visitor.name+"*</b></i>";
    190                                                         text.append(visitor.name);
    191                                                         if (osm.id != 0)
    192                                                                 text.append("<br>id="+osm.id);
    193                                                         for (Entry<String, String> e : osm.entrySet())
    194                                                                 text.append("<br>"+e.getKey()+"="+e.getValue());
    195                                                         final JLabel l = new JLabel("<html>"+text.toString()+"</html>", visitor.icon, JLabel.HORIZONTAL);
    196                                                         l.setFont(l.getFont().deriveFont(Font.PLAIN));
    197                                                         l.setVerticalTextPosition(JLabel.TOP);
    198                                                         l.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
    199                                                         l.addMouseListener(new MouseAdapter(){
    200                                                                 @Override public void mouseEntered(MouseEvent e) {
    201                                                                         l.setText("<html><u color='blue'>"+text.toString()+"</u></html>");
    202                                                                 }
    203                                                                 @Override public void mouseExited(MouseEvent e) {
    204                                                                         l.setText("<html>"+text.toString()+"</html>");
    205                                                                 }
    206                                                                 @Override public void mouseClicked(MouseEvent e) {
    207                                                                         Main.ds.setSelected(osm);
    208                                                                         mv.repaint();
    209                                                                 }
    210                                                         });
    211                                                         c.add(l, GBC.eol());
    212                                                 }
    213 
    214                                                 Point p = mv.getLocationOnScreen();
    215                                                 popup = PopupFactory.getSharedInstance().getPopup(mv, c, p.x+ms.mousePos.x+16, p.y+ms.mousePos.y+16);
    216                                                 final Popup staticPopup = popup;
    217                                                 EventQueue.invokeLater(new Runnable(){
    218                                                         public void run() {
    219                                                                 staticPopup.show();
     181                        }
     182
     183                        JPanel c = new JPanel(new GridBagLayout());
     184                        for (final OsmPrimitive osm : osms) {
     185                            NameVisitor visitor = new NameVisitor();
     186                            osm.visit(visitor);
     187                            final StringBuilder text = new StringBuilder();
     188                            if (osm.id == 0 || osm.modified)
     189                                visitor.name = "<i><b>"+visitor.name+"*</b></i>";
     190                            text.append(visitor.name);
     191                            if (osm.id != 0)
     192                                text.append("<br>id="+osm.id);
     193                            for (Entry<String, String> e : osm.entrySet())
     194                                text.append("<br>"+e.getKey()+"="+e.getValue());
     195                            final JLabel l = new JLabel("<html>"+text.toString()+"</html>", visitor.icon, JLabel.HORIZONTAL);
     196                            l.setFont(l.getFont().deriveFont(Font.PLAIN));
     197                            l.setVerticalTextPosition(JLabel.TOP);
     198                            l.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
     199                            l.addMouseListener(new MouseAdapter(){
     200                                @Override public void mouseEntered(MouseEvent e) {
     201                                    l.setText("<html><u color='blue'>"+text.toString()+"</u></html>");
     202                                }
     203                                @Override public void mouseExited(MouseEvent e) {
     204                                    l.setText("<html>"+text.toString()+"</html>");
     205                                }
     206                                @Override public void mouseClicked(MouseEvent e) {
     207                                    Main.ds.setSelected(osm);
     208                                    mv.repaint();
     209                                }
     210                            });
     211                            c.add(l, GBC.eol());
     212                        }
     213
     214                        Point p = mv.getLocationOnScreen();
     215                        popup = PopupFactory.getSharedInstance().getPopup(mv, c, p.x+ms.mousePos.x+16, p.y+ms.mousePos.y+16);
     216                        final Popup staticPopup = popup;
     217                        EventQueue.invokeLater(new Runnable(){
     218                            public void run() {
     219                                staticPopup.show();
    220220                            }
    221                                                 });
    222                                         } else if (popup != null) {
    223                                                 final Popup staticPopup = popup;
    224                                                 popup = null;
    225                                                 EventQueue.invokeLater(new Runnable(){
    226                                                         public void run() {
    227                                                                 staticPopup.hide();
     221                        });
     222                    } else if (popup != null) {
     223                        final Popup staticPopup = popup;
     224                        popup = null;
     225                        EventQueue.invokeLater(new Runnable(){
     226                            public void run() {
     227                                staticPopup.hide();
    228228                            }
    229                                                 });
    230                                         }
    231                                 } catch (ConcurrentModificationException x) {
    232                                 } catch (NullPointerException x) {
    233                                 }
    234                         }
    235                 }
    236         }
    237 
    238         /**
    239         * Everything, the collector is interested of. Access must be synchronized.
    240         * @author imi
    241         */
    242         class MouseState {
    243                 Point mousePos;
    244                 int modifiers;
    245         }
    246         /**
    247         * The last sent mouse movement event.
    248         */
    249         MouseState mouseState = new MouseState();
    250 
    251         /**
    252         * Construct a new MapStatus and attach it to the map view.
    253         * @param mapFrame The MapFrame the status line is part of.
    254         */
    255         public MapStatus(final MapFrame mapFrame) {
    256                 this.mv = mapFrame.mapView;
    257                
     229                        });
     230                    }
     231                } catch (ConcurrentModificationException x) {
     232                } catch (NullPointerException x) {
     233                }
     234            }
     235        }
     236    }
     237
     238    /**
     239    * Everything, the collector is interested of. Access must be synchronized.
     240    * @author imi
     241    */
     242    class MouseState {
     243        Point mousePos;
     244        int modifiers;
     245    }
     246    /**
     247    * The last sent mouse movement event.
     248    */
     249    MouseState mouseState = new MouseState();
     250
     251    /**
     252    * Construct a new MapStatus and attach it to the map view.
     253    * @param mapFrame The MapFrame the status line is part of.
     254    */
     255    public MapStatus(final MapFrame mapFrame) {
     256        this.mv = mapFrame.mapView;
     257
    258258        try {
    259259            mCord = LatLon.CoordinateFormat.valueOf(Main.pref.get("coordinates"));
     
    261261            mCord =LatLon.CoordinateFormat.DECIMAL_DEGREES;
    262262        }
    263                 // Listen for mouse movements and set the position text field
    264                 mv.addMouseMotionListener(new MouseMotionListener(){
    265                         public void mouseDragged(MouseEvent e) {
    266                                 mouseMoved(e);
    267                         }
    268                         public void mouseMoved(MouseEvent e) {
    269                                 if (mv.center == null)
    270                                         return;
    271                                 // Do not update the view if ctrl is pressed.
    272                                 if ((e.getModifiersEx() & MouseEvent.CTRL_DOWN_MASK) == 0) {
    273                                         LatLon p = mv.getLatLon(e.getX(),e.getY());
    274                                         latText.setText(p.latToString(mCord));
    275                                         lonText.setText(p.lonToString(mCord));
    276                                 }
    277                         }
    278                 });
    279 
    280                 setLayout(new GridBagLayout());
    281                 setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
     263        // Listen for mouse movements and set the position text field
     264        mv.addMouseMotionListener(new MouseMotionListener(){
     265            public void mouseDragged(MouseEvent e) {
     266                mouseMoved(e);
     267            }
     268            public void mouseMoved(MouseEvent e) {
     269                if (mv.center == null)
     270                    return;
     271                // Do not update the view if ctrl is pressed.
     272                if ((e.getModifiersEx() & MouseEvent.CTRL_DOWN_MASK) == 0) {
     273                    LatLon p = mv.getLatLon(e.getX(),e.getY());
     274                    latText.setText(p.latToString(mCord));
     275                    lonText.setText(p.lonToString(mCord));
     276                }
     277            }
     278        });
     279
     280        setLayout(new GridBagLayout());
     281        setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
    282282
    283283        add(latText, GBC.std());
     
    287287        add(distText, GBC.std().insets(3,0,0,0));
    288288
    289                 helpText.setEditable(false);
    290                 add(nameText, GBC.std().insets(3,0,0,0));
    291                 add(helpText, GBC.eol().insets(3,0,0,0).fill(GBC.HORIZONTAL));
    292                
    293                 // The background thread
    294                 final Collector collector = new Collector(mapFrame);
    295                 new Thread(collector).start();
    296 
    297                 // Listen to keyboard/mouse events for pressing/releasing alt key and
    298                 // inform the collector.
    299                 try {
    300                         Toolkit.getDefaultToolkit().addAWTEventListener(new AWTEventListener(){
    301                                 public void eventDispatched(AWTEvent event) {
    302                                         synchronized (collector) {
    303                                                 mouseState.modifiers = ((InputEvent)event).getModifiersEx();
    304                                                 if (event instanceof MouseEvent)
    305                                                         mouseState.mousePos = ((MouseEvent)event).getPoint();
    306                                                 collector.notify();
    307                                         }
    308                                 }
    309                         }, AWTEvent.KEY_EVENT_MASK | AWTEvent.MOUSE_EVENT_MASK | AWTEvent.MOUSE_MOTION_EVENT_MASK);
    310                 } catch (SecurityException ex) {
    311                         mapFrame.mapView.addMouseMotionListener(new MouseMotionListener() {
    312                                 public void mouseMoved(MouseEvent e) {
    313                                         synchronized (collector) {
    314                                                 mouseState.modifiers = e.getModifiersEx();
    315                                                 mouseState.mousePos = e.getPoint();
    316                                                 collector.notify();
    317                                         }
    318                                 }
    319 
    320                                 public void mouseDragged(MouseEvent e) {
    321                                         mouseMoved(e);
    322                                 }
    323                         });
    324 
    325                         mapFrame.mapView.addKeyListener(new KeyAdapter() {
    326                                 @Override public void keyPressed(KeyEvent e) {
    327                                         synchronized (collector) {
    328                                                 mouseState.modifiers = e.getModifiersEx();
    329                                                 collector.notify();
    330                                         }
    331                                 }
    332 
    333                                 @Override public void keyReleased(KeyEvent e) {
    334                                         keyPressed(e);
    335                                 }
    336                         });
    337                 }
    338         }
    339 
    340         public String helpTopic() {
    341                 return "Statusline";
    342         }
    343        
    344         public void setHelpText(String t) {
    345                 helpText.setText(t);
    346                 helpText.setToolTipText(t);
    347         }
    348         public void setAngle(double a) {
    349                 angleText.setText(a < 0 ? "--" : Math.round(a*10)/10.0 + " °");
    350         }
    351         public void setHeading(double h) {
    352                 headingText.setText(h < 0 ? "--" : Math.round(h*10)/10.0 + " °");
    353         }
    354         public void setDist(double dist) {
    355                 String text = dist > 1000 ? (Math.round(dist/100)/10.0)+" km" : Math.round(dist*10)/10.0 +" m";
    356                 distText.setText(dist < 0 ? "--" : text);
    357         }
    358        
     289        helpText.setEditable(false);
     290        add(nameText, GBC.std().insets(3,0,0,0));
     291        add(helpText, GBC.eol().insets(3,0,0,0).fill(GBC.HORIZONTAL));
     292
     293        // The background thread
     294        final Collector collector = new Collector(mapFrame);
     295        new Thread(collector).start();
     296
     297        // Listen to keyboard/mouse events for pressing/releasing alt key and
     298        // inform the collector.
     299        try {
     300            Toolkit.getDefaultToolkit().addAWTEventListener(new AWTEventListener(){
     301                public void eventDispatched(AWTEvent event) {
     302                    synchronized (collector) {
     303                        mouseState.modifiers = ((InputEvent)event).getModifiersEx();
     304                        if (event instanceof MouseEvent)
     305                            mouseState.mousePos = ((MouseEvent)event).getPoint();
     306                        collector.notify();
     307                    }
     308                }
     309            }, AWTEvent.KEY_EVENT_MASK | AWTEvent.MOUSE_EVENT_MASK | AWTEvent.MOUSE_MOTION_EVENT_MASK);
     310        } catch (SecurityException ex) {
     311            mapFrame.mapView.addMouseMotionListener(new MouseMotionListener() {
     312                public void mouseMoved(MouseEvent e) {
     313                    synchronized (collector) {
     314                        mouseState.modifiers = e.getModifiersEx();
     315                        mouseState.mousePos = e.getPoint();
     316                        collector.notify();
     317                    }
     318                }
     319
     320                public void mouseDragged(MouseEvent e) {
     321                    mouseMoved(e);
     322                }
     323            });
     324
     325            mapFrame.mapView.addKeyListener(new KeyAdapter() {
     326                @Override public void keyPressed(KeyEvent e) {
     327                    synchronized (collector) {
     328                        mouseState.modifiers = e.getModifiersEx();
     329                        collector.notify();
     330                    }
     331                }
     332
     333                @Override public void keyReleased(KeyEvent e) {
     334                    keyPressed(e);
     335                }
     336            });
     337        }
     338    }
     339
     340    public String helpTopic() {
     341        return "Statusline";
     342    }
     343
     344    public void setHelpText(String t) {
     345        helpText.setText(t);
     346        helpText.setToolTipText(t);
     347    }
     348    public void setAngle(double a) {
     349        angleText.setText(a < 0 ? "--" : Math.round(a*10)/10.0 + " °");
     350    }
     351    public void setHeading(double h) {
     352        headingText.setText(h < 0 ? "--" : Math.round(h*10)/10.0 + " °");
     353    }
     354    public void setDist(double dist) {
     355        String text = dist > 1000 ? (Math.round(dist/100)/10.0)+" km" : Math.round(dist*10)/10.0 +" m";
     356        distText.setText(dist < 0 ? "--" : text);
     357    }
     358
    359359}
  • trunk/src/org/openstreetmap/josm/gui/MapView.java

    r1084 r1169  
    5656public class MapView extends NavigatableComponent {
    5757
    58         /**
    59         * Interface to notify listeners of the change of the active layer.
    60         * @author imi
    61         * @deprecated Use Layer.LayerChangeListener instead
    62         */
    63         @Deprecated public interface LayerChangeListener {
    64                 void activeLayerChange(Layer oldLayer, Layer newLayer);
    65                 void layerAdded(Layer newLayer);
    66                 void layerRemoved(Layer oldLayer);
    67         }
    68 
    69         /**
    70         * A list of all layers currently loaded.
    71         */
    72         private ArrayList<Layer> layers = new ArrayList<Layer>();
    73         /**
    74         * The play head marker: there is only one of these so it isn't in any specific layer
    75         */
    76         public PlayHeadMarker playHeadMarker = null;
    77         /**
    78         * Direct link to the edit layer (if any) in the layers list.
    79         */
    80         public OsmDataLayer editLayer;
    81         /**
    82         * The layer from the layers list that is currently active.
    83         */
    84         private Layer activeLayer;
    85 
    86         /**
    87         * The last event performed by mouse.
    88         */
    89         public MouseEvent lastMEvent;
    90 
    91         private LinkedList<MapViewPaintable> temporaryLayers = new LinkedList<MapViewPaintable>();
    92 
    93         private BufferedImage offscreenBuffer;
    94 
    95         /**
    96         * The listener of the active layer changes.
    97         * @deprecated Use Layer.listener instead.
    98         */
    99         @Deprecated private Collection<LayerChangeListener> listeners = new LinkedList<LayerChangeListener>();
    100 
    101         public MapView() {
    102                 addComponentListener(new ComponentAdapter(){
    103                         @Override public void componentResized(ComponentEvent e) {
    104                                 removeComponentListener(this);
    105 
    106                                 if (!zoomToEditLayerBoundingBox())
    107                                         new AutoScaleAction("data").actionPerformed(null);
    108 
    109                                 new MapMover(MapView.this, Main.contentPane);
    110                                 JosmAction mv;
    111                                 mv = new MoveAction(MoveAction.Direction.UP);
    112                                 if (mv.getShortcut() != null) {
    113                                         Main.contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(mv.getShortcut().getKeyStroke(), "UP");
    114                                         Main.contentPane.getActionMap().put("UP", mv);
    115                                 }
    116                                 mv = new MoveAction(MoveAction.Direction.DOWN);
    117                                 if (mv.getShortcut() != null) {
    118                                         Main.contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(mv.getShortcut().getKeyStroke(), "DOWN");
    119                                         Main.contentPane.getActionMap().put("DOWN", mv);
    120                                 }
    121                                 mv = new MoveAction(MoveAction.Direction.LEFT);
    122                                 if (mv.getShortcut() != null) {
    123                                         Main.contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(mv.getShortcut().getKeyStroke(), "LEFT");
    124                                         Main.contentPane.getActionMap().put("LEFT", mv);
    125                                 }
    126                                 mv = new MoveAction(MoveAction.Direction.RIGHT);
    127                                 if (mv.getShortcut() != null) {
    128                                         Main.contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(mv.getShortcut().getKeyStroke(), "RIGHT");
    129                                         Main.contentPane.getActionMap().put("RIGHT", mv);
    130                                 }
    131 
    132                                 MapSlider zoomSlider = new MapSlider(MapView.this);
    133                                 add(zoomSlider);
    134                                 zoomSlider.setBounds(3, 0, 114, 30);
    135 
    136                                 MapScaler scaler = new MapScaler(MapView.this, Main.proj);
    137                                 add(scaler);
    138                                 scaler.setLocation(10,30);
    139                         }
    140                 });
    141 
    142                 // listend to selection changes to redraw the map
    143                 DataSet.selListeners.add(new SelectionChangedListener(){
    144                         public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) {
    145                                 repaint();
    146                         }
    147                 });
    148 
    149                 //store the last mouse action
    150                 this.addMouseMotionListener(new MouseMotionListener() {
    151                         public void mouseDragged(MouseEvent e) {
    152                                 mouseMoved(e);
    153                         }
    154                         public void mouseMoved(MouseEvent e) {
    155                                 lastMEvent = e;
    156                         }
    157                 });
    158         }
    159 
    160         /**
    161         * Add a layer to the current MapView. The layer will be added at topmost
    162         * position.
    163         */
    164         public void addLayer(Layer layer) {
    165                 if (layer instanceof OsmDataLayer) {
    166                         editLayer = (OsmDataLayer)layer;
    167                         Main.ds = editLayer.data;
    168                         editLayer.listenerModified.add(new ModifiedChangedListener(){
    169                                 public void modifiedChanged(boolean value, OsmDataLayer source) {
    170                                         JOptionPane.getFrameForComponent(Main.parent).setTitle((value?"*":"")+tr("Java OpenStreetMap - Editor"));
    171                                 }
    172                         });
    173                 }
    174                 if (layer instanceof MarkerLayer && playHeadMarker == null)
    175                         playHeadMarker = PlayHeadMarker.create();
    176                 int pos = layers.size();
    177                 while(pos > 0 && layers.get(pos-1).background)
    178                         --pos;
    179                 layers.add(pos, layer);
    180 
    181                 // TODO: Deprecated
    182                 for (LayerChangeListener l : listeners)
    183                         l.layerAdded(layer);
    184                 for (Layer.LayerChangeListener l : Layer.listeners)
    185                         l.layerAdded(layer);
    186                 if (layer instanceof OsmDataLayer || activeLayer == null) {
    187                         // autoselect the new layer
    188                         Layer old = activeLayer;
    189                         setActiveLayer(layer);
    190                         for (Layer.LayerChangeListener l : Layer.listeners)
    191                                 l.activeLayerChange(old, layer);
    192                 }
    193                 repaint();
    194         }
    195 
    196         @Override
    197         protected DataSet getData()
    198         {
    199                 if(activeLayer != null && activeLayer instanceof OsmDataLayer)
    200                         return ((OsmDataLayer)activeLayer).data;
    201                 return new DataSet();
    202         }
    203 
    204         public Boolean isDrawableLayer()
    205         {
    206                 return activeLayer != null && activeLayer instanceof OsmDataLayer;
    207         }
    208 
    209         /**
    210         * Remove the layer from the mapview. If the layer was in the list before,
    211         * an LayerChange event is fired.
    212         */
    213         public void removeLayer(Layer layer) {
    214                 if (layers.remove(layer)) {
    215                         // TODO: Deprecated
    216                         for (LayerChangeListener l : listeners)
    217                                 l.layerRemoved(layer);
    218                         for (Layer.LayerChangeListener l : Layer.listeners)
    219                                 l.layerRemoved(layer);
    220                 }
    221                 if (layer == editLayer) {
    222                         editLayer = null;
    223                         Main.ds.setSelected();
    224                 }
    225                 layer.destroy();
    226         }
    227 
    228         private Boolean virtualnodes = false;
    229         public void enableVirtualNodes(Boolean state)
    230         {
    231                 if(virtualnodes != state)
    232                 {
    233                         virtualnodes = state;
    234                         repaint();
    235                 }
    236         }
    237         public Boolean useVirtualNodes()
    238         {
    239                 return virtualnodes;
    240         }
    241 
    242         /**
    243         * Moves the layer to the given new position. No event is fired.
    244          * @param layer         The layer to move
    245          * @param pos           The new position of the layer
    246         */
    247         public void moveLayer(Layer layer, int pos) {
    248                 int curLayerPos = layers.indexOf(layer);
    249                 if (curLayerPos == -1)
    250                         throw new IllegalArgumentException(tr("layer not in list."));
    251                 if (pos == curLayerPos)
    252                         return; // already in place.
    253                 layers.remove(curLayerPos);
    254                 if (pos >= layers.size())
    255                         layers.add(layer);
    256                 else
    257                         layers.add(pos, layer);
    258         }
    259 
    260         /**
    261         * Draw the component.
    262         */
    263         @Override public void paint(Graphics g) {
    264                 if (center == null)
    265                         return; // no data loaded yet.
    266 
    267                 // re-create offscreen-buffer if we've been resized, otherwise
    268                 // just re-use it.
    269                 if (null == offscreenBuffer || offscreenBuffer.getWidth() != getWidth()
    270                         || offscreenBuffer.getHeight() != getHeight())
    271                         offscreenBuffer = new BufferedImage(getWidth(), getHeight(),
    272                                 BufferedImage.TYPE_INT_ARGB);
    273 
    274                 Graphics2D tempG = offscreenBuffer.createGraphics();
    275                 tempG.setColor(Main.pref.getColor("background", Color.BLACK));
    276                 tempG.fillRect(0, 0, getWidth(), getHeight());
    277 
    278                 for (int i = layers.size()-1; i >= 0; --i) {
    279                         Layer l = layers.get(i);
    280                         if (l.visible/* && l != getActiveLayer()*/)
    281                                 l.paint(tempG, this);
    282                 }
    283 
    284                 /*if (getActiveLayer() != null && getActiveLayer().visible)
    285                         getActiveLayer().paint(tempG, this);*/
    286 
    287                 for (MapViewPaintable mvp : temporaryLayers) {
    288                         mvp.paint(tempG, this);
    289                 }
    290 
    291                 // draw world borders
    292                 tempG.setColor(Color.WHITE);
    293                 Bounds b = new Bounds();
    294                 Point min = getPoint(getProjection().latlon2eastNorth(b.min));
    295                 Point max = getPoint(getProjection().latlon2eastNorth(b.max));
    296                 int x1 = Math.min(min.x, max.x);
    297                 int y1 = Math.min(min.y, max.y);
    298                 int x2 = Math.max(min.x, max.x);
    299                 int y2 = Math.max(min.y, max.y);
    300                 if (x1 > 0 || y1 > 0 || x2 < getWidth() || y2 < getHeight())
    301                         tempG.drawRect(x1, y1, x2-x1+1, y2-y1+1);
    302 
    303                 if (playHeadMarker != null)
    304                         playHeadMarker.paint(tempG, this);
    305 
    306                 g.drawImage(offscreenBuffer, 0, 0, null);
    307                 super.paint(g);
    308         }
    309 
    310         /**
    311         * Set the new dimension to the projection class. Also adjust the components
    312         * scale, if in autoScale mode.
    313         */
    314         public void recalculateCenterScale(BoundingXYVisitor box) {
    315                 // -20 to leave some border
    316                 int w = getWidth()-20;
    317                 if (w < 20)
    318                         w = 20;
    319                 int h = getHeight()-20;
    320                 if (h < 20)
    321                         h = 20;
    322 
    323                 EastNorth oldCenter = center;
    324                 double oldScale = this.scale;
    325 
    326                 if (box == null || box.min == null || box.max == null || box.min.equals(box.max)) {
    327                         // no bounds means whole world
    328                         center = getProjection().latlon2eastNorth(new LatLon(0,0));
    329                         EastNorth world = getProjection().latlon2eastNorth(new LatLon(Projection.MAX_LAT,Projection.MAX_LON));
    330                         double scaleX = world.east()*2/w;
    331                         double scaleY = world.north()*2/h;
    332                         scale = Math.max(scaleX, scaleY); // minimum scale to see all of the screen
    333                 } else {
    334                         center = new EastNorth(box.min.east()/2+box.max.east()/2, box.min.north()/2+box.max.north()/2);
    335                         double scaleX = (box.max.east()-box.min.east())/w;
    336                         double scaleY = (box.max.north()-box.min.north())/h;
    337                         scale = Math.max(scaleX, scaleY); // minimum scale to see all of the screen
    338                 }
    339 
    340                 if (!center.equals(oldCenter))
    341                         firePropertyChange("center", oldCenter, center);
    342                 if (oldScale != scale)
    343                         firePropertyChange("scale", oldScale, scale);
    344                 repaint();
    345         }
    346 
    347         /**
    348         * Add a listener for changes of active layer.
    349         * @param listener The listener that get added.
    350         * @deprecated Use Layer.listener.add instead.
    351         */
    352         @Deprecated public void addLayerChangeListener(LayerChangeListener listener) {
    353                 if (listener != null)
    354                         listeners.add(listener);
    355         }
    356 
    357         /**
    358         * Remove the listener.
    359         * @param listener The listener that get removed from the list.
    360         * @deprecated Use Layer.listener.remove instead
    361         */
    362         @Deprecated public void removeLayerChangeListener(LayerChangeListener listener) {
    363                 listeners.remove(listener);
    364         }
    365 
    366         /**
    367         * @return An unmodificable list of all layers
    368         */
    369         public Collection<Layer> getAllLayers() {
    370                 return Collections.unmodifiableCollection(layers);
    371         }
    372 
    373         /**
    374         * Set the active selection to the given value and raise an layerchange event.
    375         */
    376         public void setActiveLayer(Layer layer) {
    377                 if (!layers.contains(layer))
    378                         throw new IllegalArgumentException("Layer must be in layerlist");
    379                 if (layer instanceof OsmDataLayer) {
    380                         editLayer = (OsmDataLayer)layer;
    381                         Main.ds = editLayer.data;
    382                 }
    383                 else
    384                         Main.ds.setSelected();
    385                 DataSet.fireSelectionChanged(Main.ds.getSelected());
    386                 Layer old = activeLayer;
    387                 activeLayer = layer;
    388                 if (old != layer) {
    389                         // TODO: Deprecated
    390                         for (LayerChangeListener l : listeners)
    391                                 l.activeLayerChange(old, layer);
    392                         for (Layer.LayerChangeListener l : Layer.listeners)
    393                                 l.activeLayerChange(old, layer);
    394                 }
    395                 repaint();
    396         }
    397 
    398         /**
    399         * @return The current active layer
    400         */
    401         public Layer getActiveLayer() {
    402                 return activeLayer;
    403         }
    404 
    405         /**
    406         * In addition to the base class funcitonality, this keep trak of the autoscale
    407         * feature.
    408         */
    409         @Override public void zoomTo(EastNorth newCenter, double scale) {
    410                 EastNorth oldCenter = center;
    411                 double oldScale = this.scale;
    412                 super.zoomTo(newCenter, scale);
    413                 if ((oldCenter == null && center != null) || !oldCenter.equals(center))
    414                         firePropertyChange("center", oldCenter, center);
    415                 if (oldScale != scale)
    416                         firePropertyChange("scale", oldScale, scale);
    417         }
    418 
    419         /**
    420         * Tries to zoom to the download boundingbox[es] of the current edit layer
    421         * (aka {@link OsmDataLayer}). If the edit layer has multiple download bounding
    422         * boxes it zooms to a large virtual bounding box containing all smaller ones.
    423         * This implementation can be used for resolving ticket #1461.
    424         *
    425         * @return <code>true</code> if a zoom operation has been performed
    426         * @author Jan Peter Stotz
    427         */
    428         public boolean zoomToEditLayerBoundingBox() {
    429                 // workaround for #1461 (zoom to download bounding box instead of all data)
    430                 // In case we already have an existing data layer ...
    431                 Collection<DataSource> dataSources = Main.main.editLayer().data.dataSources;
    432                 // ... with bounding box[es] of data loaded from OSM or a file...
    433                 BoundingXYVisitor bbox = new BoundingXYVisitor();
    434                 for (DataSource ds : dataSources) {
    435                         if (ds.bounds != null) {
    436                                 bbox.visit(Main.proj.latlon2eastNorth(ds.bounds.max));
    437                                 bbox.visit(Main.proj.latlon2eastNorth(ds.bounds.min));
    438                         }
    439                         if (bbox.max != null && bbox.min != null && !bbox.max.equals(bbox.min)) {
    440                                 // ... we zoom to it's bounding box
    441                                 recalculateCenterScale(bbox);
    442                                 return true;
    443                         }
    444                 }
    445                 return false;
    446         }
    447 
    448         public boolean addTemporaryLayer(MapViewPaintable mvp) {
    449                 if (temporaryLayers.contains(mvp)) return false;
    450                 return temporaryLayers.add(mvp);
    451         }
    452 
    453         public boolean removeTemporaryLayer(MapViewPaintable mvp) {
    454                 return temporaryLayers.remove(mvp);
    455         }
     58    /**
     59    * Interface to notify listeners of the change of the active layer.
     60    * @author imi
     61    * @deprecated Use Layer.LayerChangeListener instead
     62    */
     63    @Deprecated public interface LayerChangeListener {
     64        void activeLayerChange(Layer oldLayer, Layer newLayer);
     65        void layerAdded(Layer newLayer);
     66        void layerRemoved(Layer oldLayer);
     67    }
     68
     69    /**
     70    * A list of all layers currently loaded.
     71    */
     72    private ArrayList<Layer> layers = new ArrayList<Layer>();
     73    /**
     74    * The play head marker: there is only one of these so it isn't in any specific layer
     75    */
     76    public PlayHeadMarker playHeadMarker = null;
     77    /**
     78    * Direct link to the edit layer (if any) in the layers list.
     79    */
     80    public OsmDataLayer editLayer;
     81    /**
     82    * The layer from the layers list that is currently active.
     83    */
     84    private Layer activeLayer;
     85
     86    /**
     87    * The last event performed by mouse.
     88    */
     89    public MouseEvent lastMEvent;
     90
     91    private LinkedList<MapViewPaintable> temporaryLayers = new LinkedList<MapViewPaintable>();
     92
     93    private BufferedImage offscreenBuffer;
     94
     95    /**
     96    * The listener of the active layer changes.
     97    * @deprecated Use Layer.listener instead.
     98    */
     99    @Deprecated private Collection<LayerChangeListener> listeners = new LinkedList<LayerChangeListener>();
     100
     101    public MapView() {
     102        addComponentListener(new ComponentAdapter(){
     103            @Override public void componentResized(ComponentEvent e) {
     104                removeComponentListener(this);
     105
     106                if (!zoomToEditLayerBoundingBox())
     107                    new AutoScaleAction("data").actionPerformed(null);
     108
     109                new MapMover(MapView.this, Main.contentPane);
     110                JosmAction mv;
     111                mv = new MoveAction(MoveAction.Direction.UP);
     112                if (mv.getShortcut() != null) {
     113                    Main.contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(mv.getShortcut().getKeyStroke(), "UP");
     114                    Main.contentPane.getActionMap().put("UP", mv);
     115                }
     116                mv = new MoveAction(MoveAction.Direction.DOWN);
     117                if (mv.getShortcut() != null) {
     118                    Main.contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(mv.getShortcut().getKeyStroke(), "DOWN");
     119                    Main.contentPane.getActionMap().put("DOWN", mv);
     120                }
     121                mv = new MoveAction(MoveAction.Direction.LEFT);
     122                if (mv.getShortcut() != null) {
     123                    Main.contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(mv.getShortcut().getKeyStroke(), "LEFT");
     124                    Main.contentPane.getActionMap().put("LEFT", mv);
     125                }
     126                mv = new MoveAction(MoveAction.Direction.RIGHT);
     127                if (mv.getShortcut() != null) {
     128                    Main.contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(mv.getShortcut().getKeyStroke(), "RIGHT");
     129                    Main.contentPane.getActionMap().put("RIGHT", mv);
     130                }
     131
     132                MapSlider zoomSlider = new MapSlider(MapView.this);
     133                add(zoomSlider);
     134                zoomSlider.setBounds(3, 0, 114, 30);
     135
     136                MapScaler scaler = new MapScaler(MapView.this, Main.proj);
     137                add(scaler);
     138                scaler.setLocation(10,30);
     139            }
     140        });
     141
     142        // listend to selection changes to redraw the map
     143        DataSet.selListeners.add(new SelectionChangedListener(){
     144            public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) {
     145                repaint();
     146            }
     147        });
     148
     149        //store the last mouse action
     150        this.addMouseMotionListener(new MouseMotionListener() {
     151            public void mouseDragged(MouseEvent e) {
     152                mouseMoved(e);
     153            }
     154            public void mouseMoved(MouseEvent e) {
     155                lastMEvent = e;
     156            }
     157        });
     158    }
     159
     160    /**
     161    * Add a layer to the current MapView. The layer will be added at topmost
     162    * position.
     163    */
     164    public void addLayer(Layer layer) {
     165        if (layer instanceof OsmDataLayer) {
     166            editLayer = (OsmDataLayer)layer;
     167            Main.ds = editLayer.data;
     168            editLayer.listenerModified.add(new ModifiedChangedListener(){
     169                public void modifiedChanged(boolean value, OsmDataLayer source) {
     170                    JOptionPane.getFrameForComponent(Main.parent).setTitle((value?"*":"")+tr("Java OpenStreetMap - Editor"));
     171                }
     172            });
     173        }
     174        if (layer instanceof MarkerLayer && playHeadMarker == null)
     175            playHeadMarker = PlayHeadMarker.create();
     176        int pos = layers.size();
     177        while(pos > 0 && layers.get(pos-1).background)
     178            --pos;
     179        layers.add(pos, layer);
     180
     181        // TODO: Deprecated
     182        for (LayerChangeListener l : listeners)
     183            l.layerAdded(layer);
     184        for (Layer.LayerChangeListener l : Layer.listeners)
     185            l.layerAdded(layer);
     186        if (layer instanceof OsmDataLayer || activeLayer == null) {
     187            // autoselect the new layer
     188            Layer old = activeLayer;
     189            setActiveLayer(layer);
     190            for (Layer.LayerChangeListener l : Layer.listeners)
     191                l.activeLayerChange(old, layer);
     192        }
     193        repaint();
     194    }
     195
     196    @Override
     197    protected DataSet getData()
     198    {
     199        if(activeLayer != null && activeLayer instanceof OsmDataLayer)
     200            return ((OsmDataLayer)activeLayer).data;
     201        return new DataSet();
     202    }
     203
     204    public Boolean isDrawableLayer()
     205    {
     206        return activeLayer != null && activeLayer instanceof OsmDataLayer;
     207    }
     208
     209    /**
     210    * Remove the layer from the mapview. If the layer was in the list before,
     211    * an LayerChange event is fired.
     212    */
     213    public void removeLayer(Layer layer) {
     214        if (layers.remove(layer)) {
     215            // TODO: Deprecated
     216            for (LayerChangeListener l : listeners)
     217                l.layerRemoved(layer);
     218            for (Layer.LayerChangeListener l : Layer.listeners)
     219                l.layerRemoved(layer);
     220        }
     221        if (layer == editLayer) {
     222            editLayer = null;
     223            Main.ds.setSelected();
     224        }
     225        layer.destroy();
     226    }
     227
     228    private Boolean virtualnodes = false;
     229    public void enableVirtualNodes(Boolean state)
     230    {
     231        if(virtualnodes != state)
     232        {
     233            virtualnodes = state;
     234            repaint();
     235        }
     236    }
     237    public Boolean useVirtualNodes()
     238    {
     239        return virtualnodes;
     240    }
     241
     242    /**
     243    * Moves the layer to the given new position. No event is fired.
     244     * @param layer     The layer to move
     245     * @param pos       The new position of the layer
     246    */
     247    public void moveLayer(Layer layer, int pos) {
     248        int curLayerPos = layers.indexOf(layer);
     249        if (curLayerPos == -1)
     250            throw new IllegalArgumentException(tr("layer not in list."));
     251        if (pos == curLayerPos)
     252            return; // already in place.
     253        layers.remove(curLayerPos);
     254        if (pos >= layers.size())
     255            layers.add(layer);
     256        else
     257            layers.add(pos, layer);
     258    }
     259
     260    /**
     261    * Draw the component.
     262    */
     263    @Override public void paint(Graphics g) {
     264        if (center == null)
     265            return; // no data loaded yet.
     266
     267        // re-create offscreen-buffer if we've been resized, otherwise
     268        // just re-use it.
     269        if (null == offscreenBuffer || offscreenBuffer.getWidth() != getWidth()
     270                || offscreenBuffer.getHeight() != getHeight())
     271            offscreenBuffer = new BufferedImage(getWidth(), getHeight(),
     272                    BufferedImage.TYPE_INT_ARGB);
     273
     274        Graphics2D tempG = offscreenBuffer.createGraphics();
     275        tempG.setColor(Main.pref.getColor("background", Color.BLACK));
     276        tempG.fillRect(0, 0, getWidth(), getHeight());
     277
     278        for (int i = layers.size()-1; i >= 0; --i) {
     279            Layer l = layers.get(i);
     280            if (l.visible/* && l != getActiveLayer()*/)
     281                l.paint(tempG, this);
     282        }
     283
     284        /*if (getActiveLayer() != null && getActiveLayer().visible)
     285            getActiveLayer().paint(tempG, this);*/
     286
     287        for (MapViewPaintable mvp : temporaryLayers) {
     288            mvp.paint(tempG, this);
     289        }
     290
     291        // draw world borders
     292        tempG.setColor(Color.WHITE);
     293        Bounds b = new Bounds();
     294        Point min = getPoint(getProjection().latlon2eastNorth(b.min));
     295        Point max = getPoint(getProjection().latlon2eastNorth(b.max));
     296        int x1 = Math.min(min.x, max.x);
     297        int y1 = Math.min(min.y, max.y);
     298        int x2 = Math.max(min.x, max.x);
     299        int y2 = Math.max(min.y, max.y);
     300        if (x1 > 0 || y1 > 0 || x2 < getWidth() || y2 < getHeight())
     301            tempG.drawRect(x1, y1, x2-x1+1, y2-y1+1);
     302
     303        if (playHeadMarker != null)
     304            playHeadMarker.paint(tempG, this);
     305
     306        g.drawImage(offscreenBuffer, 0, 0, null);
     307        super.paint(g);
     308    }
     309
     310    /**
     311    * Set the new dimension to the projection class. Also adjust the components
     312    * scale, if in autoScale mode.
     313    */
     314    public void recalculateCenterScale(BoundingXYVisitor box) {
     315        // -20 to leave some border
     316        int w = getWidth()-20;
     317        if (w < 20)
     318            w = 20;
     319        int h = getHeight()-20;
     320        if (h < 20)
     321            h = 20;
     322
     323        EastNorth oldCenter = center;
     324        double oldScale = this.scale;
     325
     326        if (box == null || box.min == null || box.max == null || box.min.equals(box.max)) {
     327            // no bounds means whole world
     328            center = getProjection().latlon2eastNorth(new LatLon(0,0));
     329            EastNorth world = getProjection().latlon2eastNorth(new LatLon(Projection.MAX_LAT,Projection.MAX_LON));
     330            double scaleX = world.east()*2/w;
     331            double scaleY = world.north()*2/h;
     332            scale = Math.max(scaleX, scaleY); // minimum scale to see all of the screen
     333        } else {
     334            center = new EastNorth(box.min.east()/2+box.max.east()/2, box.min.north()/2+box.max.north()/2);
     335            double scaleX = (box.max.east()-box.min.east())/w;
     336            double scaleY = (box.max.north()-box.min.north())/h;
     337            scale = Math.max(scaleX, scaleY); // minimum scale to see all of the screen
     338        }
     339
     340        if (!center.equals(oldCenter))
     341            firePropertyChange("center", oldCenter, center);
     342        if (oldScale != scale)
     343            firePropertyChange("scale", oldScale, scale);
     344        repaint();
     345    }
     346
     347    /**
     348    * Add a listener for changes of active layer.
     349    * @param listener The listener that get added.
     350    * @deprecated Use Layer.listener.add instead.
     351    */
     352    @Deprecated public void addLayerChangeListener(LayerChangeListener listener) {
     353        if (listener != null)
     354            listeners.add(listener);
     355    }
     356
     357    /**
     358    * Remove the listener.
     359    * @param listener The listener that get removed from the list.
     360    * @deprecated Use Layer.listener.remove instead
     361    */
     362    @Deprecated public void removeLayerChangeListener(LayerChangeListener listener) {
     363        listeners.remove(listener);
     364    }
     365
     366    /**
     367    * @return An unmodificable list of all layers
     368    */
     369    public Collection<Layer> getAllLayers() {
     370        return Collections.unmodifiableCollection(layers);
     371    }
     372
     373    /**
     374    * Set the active selection to the given value and raise an layerchange event.
     375    */
     376    public void setActiveLayer(Layer layer) {
     377        if (!layers.contains(layer))
     378            throw new IllegalArgumentException("Layer must be in layerlist");
     379        if (layer instanceof OsmDataLayer) {
     380            editLayer = (OsmDataLayer)layer;
     381            Main.ds = editLayer.data;
     382        }
     383        else
     384            Main.ds.setSelected();
     385        DataSet.fireSelectionChanged(Main.ds.getSelected());
     386        Layer old = activeLayer;
     387        activeLayer = layer;
     388        if (old != layer) {
     389            // TODO: Deprecated
     390            for (LayerChangeListener l : listeners)
     391                l.activeLayerChange(old, layer);
     392            for (Layer.LayerChangeListener l : Layer.listeners)
     393                l.activeLayerChange(old, layer);
     394        }
     395        repaint();
     396    }
     397
     398    /**
     399    * @return The current active layer
     400    */
     401    public Layer getActiveLayer() {
     402        return activeLayer;
     403    }
     404
     405    /**
     406    * In addition to the base class funcitonality, this keep trak of the autoscale
     407    * feature.
     408    */
     409    @Override public void zoomTo(EastNorth newCenter, double scale) {
     410        EastNorth oldCenter = center;
     411        double oldScale = this.scale;
     412        super.zoomTo(newCenter, scale);
     413        if ((oldCenter == null && center != null) || !oldCenter.equals(center))
     414            firePropertyChange("center", oldCenter, center);
     415        if (oldScale != scale)
     416            firePropertyChange("scale", oldScale, scale);
     417    }
     418
     419    /**
     420    * Tries to zoom to the download boundingbox[es] of the current edit layer
     421    * (aka {@link OsmDataLayer}). If the edit layer has multiple download bounding
     422    * boxes it zooms to a large virtual bounding box containing all smaller ones.
     423    * This implementation can be used for resolving ticket #1461.
     424    *
     425    * @return <code>true</code> if a zoom operation has been performed
     426    * @author Jan Peter Stotz
     427    */
     428    public boolean zoomToEditLayerBoundingBox() {
     429        // workaround for #1461 (zoom to download bounding box instead of all data)
     430        // In case we already have an existing data layer ...
     431        Collection<DataSource> dataSources = Main.main.editLayer().data.dataSources;
     432        // ... with bounding box[es] of data loaded from OSM or a file...
     433        BoundingXYVisitor bbox = new BoundingXYVisitor();
     434        for (DataSource ds : dataSources) {
     435            if (ds.bounds != null) {
     436                bbox.visit(Main.proj.latlon2eastNorth(ds.bounds.max));
     437                bbox.visit(Main.proj.latlon2eastNorth(ds.bounds.min));
     438            }
     439            if (bbox.max != null && bbox.min != null && !bbox.max.equals(bbox.min)) {
     440                // ... we zoom to it's bounding box
     441                recalculateCenterScale(bbox);
     442                return true;
     443            }
     444        }
     445        return false;
     446    }
     447
     448    public boolean addTemporaryLayer(MapViewPaintable mvp) {
     449        if (temporaryLayers.contains(mvp)) return false;
     450        return temporaryLayers.add(mvp);
     451    }
     452
     453    public boolean removeTemporaryLayer(MapViewPaintable mvp) {
     454        return temporaryLayers.remove(mvp);
     455    }
    456456}
  • trunk/src/org/openstreetmap/josm/gui/NavigatableComponent.java

    r860 r1169  
    3333public class NavigatableComponent extends JComponent implements Helpful {
    3434
    35         public static final EastNorth world = Main.proj.latlon2eastNorth(new LatLon(Projection.MAX_LAT, Projection.MAX_LON));
    36         public static final int snapDistance = sqr(Main.pref.getInteger("node.snap-distance", 10));
    37 
    38         private static int sqr(int a) { return a*a;}
    39         /**
    40         * The scale factor in x or y-units per pixel. This means, if scale = 10,
    41         * every physical pixel on screen are 10 x or 10 y units in the
    42         * northing/easting space of the projection.
    43         */
    44         protected double scale;
    45         /**
    46         * Center n/e coordinate of the desired screen center.
    47         */
    48         protected EastNorth center;
    49 
    50         public NavigatableComponent() {
    51                 setLayout(null);
    52         }
    53 
    54         protected DataSet getData()
    55         {
    56                 return Main.ds;
    57         }
    58 
    59         /**
    60         * Return the OSM-conform zoom factor (0 for whole world, 1 for half, 2 for quarter...)
    61         */
    62         public int zoom() {
    63                 double sizex = scale * getWidth();
    64                 double sizey = scale * getHeight();
    65                 for (int zoom = 0; zoom <= 32; zoom++, sizex *= 2, sizey *= 2)
    66                         if (sizex > world.east() || sizey > world.north())
    67                                 return zoom;
    68                 return 32;
    69         }
    70 
    71         /**
    72         * Return the current scale value.
    73         * @return The scale value currently used in display
    74         */
    75         public double getScale() {
    76                 return scale;
    77         }
    78 
    79         /**
    80         * @return Returns the center point. A copy is returned, so users cannot
    81          *              change the center by accessing the return value. Use zoomTo instead.
    82         */
    83         public EastNorth getCenter() {
    84                 return center;
    85         }
    86 
    87         /**
    88         * @param x X-Pixelposition to get coordinate from
    89         * @param y Y-Pixelposition to get coordinate from
    90         *
    91         * @return Geographic coordinates from a specific pixel coordination
    92          *              on the screen.
    93         */
    94         public EastNorth getEastNorth(int x, int y) {
    95                 return new EastNorth(
    96                                 center.east() + (x - getWidth()/2.0)*scale,
    97                                 center.north() - (y - getHeight()/2.0)*scale);
    98         }
    99 
    100         /**
    101         * @param x X-Pixelposition to get coordinate from
    102         * @param y Y-Pixelposition to get coordinate from
    103         *
    104         * @return Geographic unprojected coordinates from a specific pixel coordination
    105          *              on the screen.
    106         */
    107         public LatLon getLatLon(int x, int y) {
    108 
    109                 return getProjection().eastNorth2latlon(getEastNorth(x, y));
    110         }
    111 
    112         /**
    113         * Return the point on the screen where this Coordinate would be.
    114         * @param p The point, where this geopoint would be drawn.
    115         * @return The point on screen where "point" would be drawn, relative
    116          *              to the own top/left.
    117         */
    118         public Point getPoint(EastNorth p) {
    119                 if(null == p)
    120                         return new Point();
    121                 double x = (p.east()-center.east())/scale + getWidth()/2;
    122                 double y = (center.north()-p.north())/scale + getHeight()/2;
    123                 return new Point((int)x,(int)y);
    124         }
    125 
    126         /**
    127         * Zoom to the given coordinate.
    128         * @param newCenter The center x-value (easting) to zoom to.
    129         * @param scale The scale to use.
    130         */
    131         public void zoomTo(EastNorth newCenter, double scale) {
    132                 center = newCenter;
    133                 getProjection().eastNorth2latlon(center);
    134                 this.scale = scale;
    135                 repaint();
    136         }
    137 
    138         /**
    139         * Return the nearest point to the screen point given.
    140         * If a node within 10 pixel is found, the nearest node is returned.
    141         */
    142         public final Node getNearestNode(Point p) {
    143                 double minDistanceSq = Double.MAX_VALUE;
    144                 Node minPrimitive = null;
    145                 for (Node n : getData().nodes) {
    146                         if (n.deleted || n.incomplete)
    147                                 continue;
    148                         Point sp = getPoint(n.eastNorth);
    149                         double dist = p.distanceSq(sp);
    150                         if (minDistanceSq > dist && dist < snapDistance) {
    151                                 minDistanceSq = p.distanceSq(sp);
    152                                 minPrimitive = n;
    153                         }
    154                         // prefer already selected node when multiple nodes on one point
    155                         else if(minDistanceSq == dist && n.selected && !minPrimitive.selected)
    156                         {
    157                                 minPrimitive = n;
    158                         }
    159                 }
    160                 return minPrimitive;
    161         }
    162 
    163         /**
    164         * @return all way segments within 10px of p, sorted by their
    165         * perpendicular distance.
    166          *
    167         * @param p the point for which to search the nearest segment.
    168         */
    169         public final List<WaySegment> getNearestWaySegments(Point p) {
    170                 TreeMap<Double, List<WaySegment>> nearest = new TreeMap<Double, List<WaySegment>>();
    171                 for (Way w : getData().ways) {
    172                         if (w.deleted || w.incomplete) continue;
    173                         Node lastN = null;
    174                         int i = -2;
    175                         for (Node n : w.nodes) {
    176                                 i++;
    177                                 if (n.deleted || n.incomplete) continue;
    178                                 if (lastN == null) {
    179                                         lastN = n;
    180                                         continue;
    181                                 }
    182 
    183                                 Point A = getPoint(lastN.eastNorth);
    184                                 Point B = getPoint(n.eastNorth);
    185                                 double c = A.distanceSq(B);
    186                                 double a = p.distanceSq(B);
    187                                 double b = p.distanceSq(A);
    188                                 double perDist = a-(a-b+c)*(a-b+c)/4/c; // perpendicular distance squared
    189                                 if (perDist < snapDistance && a < c+snapDistance && b < c+snapDistance) {
    190                                         if(w.selected) // prefer selected ways a little bit
    191                                                 perDist -= 0.00001;
    192                                         List<WaySegment> l;
    193                                         if (nearest.containsKey(perDist)) {
    194                                                 l = nearest.get(perDist);
    195                                         } else {
    196                                                 l = new LinkedList<WaySegment>();
    197                                                 nearest.put(perDist, l);
    198                                         }
    199                                         l.add(new WaySegment(w, i));
    200                                 }
    201 
    202                                 lastN = n;
    203                         }
    204                 }
    205                 ArrayList<WaySegment> nearestList = new ArrayList<WaySegment>();
    206                 for (List<WaySegment> wss : nearest.values()) {
    207                         nearestList.addAll(wss);
    208                 }
    209                 return nearestList;
    210         }
    211 
    212         /**
    213          * @return the nearest way segment to the screen point given that is not
    214         * in ignore.
    215          *
    216         * @param p the point for which to search the nearest segment.
    217         * @param ignore a collection of segments which are not to be returned.
    218         * May be null.
    219         */
    220         public final WaySegment getNearestWaySegment(Point p, Collection<WaySegment> ignore) {
    221                 List<WaySegment> nearest = getNearestWaySegments(p);
    222                 if (ignore != null) nearest.removeAll(ignore);
    223                 return nearest.isEmpty() ? null : nearest.get(0);
    224         }
    225 
    226         /**
    227         * @return the nearest way segment to the screen point given.
    228         */
    229         public final WaySegment getNearestWaySegment(Point p) {
    230                 return getNearestWaySegment(p, null);
    231         }
    232        
    233         /**
    234         * @return the nearest way to the screen point given.
    235         */
    236         public final Way getNearestWay(Point p) {
    237                 WaySegment nearestWaySeg = getNearestWaySegment(p);
    238                 return nearestWaySeg == null ? null : nearestWaySeg.way;
    239         }
    240 
    241         /**
    242         * Return the object, that is nearest to the given screen point.
    243         *
    244         * First, a node will be searched. If a node within 10 pixel is found, the
    245         * nearest node is returned.
    246         *
    247         * If no node is found, search for near ways.
    248         *
    249         * If nothing is found, return <code>null</code>.
    250         *
    251         * @param p The point on screen.
    252         * @return  The primitive that is nearest to the point p.
    253         */
    254         public OsmPrimitive getNearest(Point p) {
    255                 OsmPrimitive osm = getNearestNode(p);
    256                 if (osm == null)
    257                 {
    258                         osm = getNearestWay(p);
    259                 }
    260                 return osm;
    261         }
    262 
    263         /**
    264         * Returns a singleton of the nearest object, or else an empty collection.
    265         */
    266         public Collection<OsmPrimitive> getNearestCollection(Point p) {
    267                 OsmPrimitive osm = getNearest(p);
    268                 if (osm == null)
    269                         return Collections.emptySet();
    270                 return Collections.singleton(osm);
    271         }
    272 
    273         /**
    274         * @return A list of all objects that are nearest to
    275         * the mouse.  Does a simple sequential scan on all the data.
    276         *
    277         * @return A collection of all items or <code>null</code>
    278          *              if no item under or near the point. The returned
    279          *              list is never empty.
    280         */
    281         public Collection<OsmPrimitive> getAllNearest(Point p) {
    282                 Collection<OsmPrimitive> nearest = new HashSet<OsmPrimitive>();
    283                         for (Way w : getData().ways) {
    284                         if (w.deleted || w.incomplete) continue;
    285                         Node lastN = null;
    286                         for (Node n : w.nodes) {
    287                                 if (n.deleted || n.incomplete) continue;
    288                                 if (lastN == null) {
    289                                         lastN = n;
    290                                         continue;
    291                                 }
    292                                 Point A = getPoint(lastN.eastNorth);
    293                                 Point B = getPoint(n.eastNorth);
    294                                 double c = A.distanceSq(B);
    295                                 double a = p.distanceSq(B);
    296                                 double b = p.distanceSq(A);
    297                                 double perDist = a-(a-b+c)*(a-b+c)/4/c; // perpendicular distance squared
    298                                 if (perDist < snapDistance && a < c+snapDistance && b < c+snapDistance) {
    299                                         nearest.add(w);
    300                                                 break;
    301                                         }
    302                                 lastN = n;
    303                                 }
    304                         }
    305                 for (Node n : getData().nodes) {
    306                         if (!n.deleted && !n.incomplete
    307                                         && getPoint(n.eastNorth).distanceSq(p) < snapDistance) {
    308                                 nearest.add(n);
    309                         }
    310                 }
    311                 return nearest.isEmpty() ? null : nearest;
    312         }
    313 
    314         /**
    315         * @return A list of all nodes that are nearest to
    316         * the mouse.  Does a simple sequential scan on all the data.
    317         *
    318         * @return A collection of all nodes or <code>null</code>
    319          *              if no node under or near the point. The returned
    320          *              list is never empty.
    321         */
    322         public Collection<Node> getNearestNodes(Point p) {
    323                 Collection<Node> nearest = new HashSet<Node>();
    324                 for (Node n : getData().nodes) {
    325                         if (!n.deleted && !n.incomplete
    326                                         && getPoint(n.eastNorth).distanceSq(p) < snapDistance) {
    327                                 nearest.add(n);
    328                         }
    329                 }
    330                 return nearest.isEmpty() ? null : nearest;
    331         }
    332 
    333         /**
    334          * @return the nearest nodes to the screen point given that is not
    335         * in ignore.
    336          *
    337         * @param p the point for which to search the nearest segment.
    338         * @param ignore a collection of nodes which are not to be returned.
    339         * May be null.
    340         */
    341         public final Collection<Node> getNearestNodes(Point p, Collection<Node> ignore) {
    342                 Collection<Node> nearest = getNearestNodes(p);
     35    public static final EastNorth world = Main.proj.latlon2eastNorth(new LatLon(Projection.MAX_LAT, Projection.MAX_LON));
     36    public static final int snapDistance = sqr(Main.pref.getInteger("node.snap-distance", 10));
     37
     38    private static int sqr(int a) { return a*a;}
     39    /**
     40    * The scale factor in x or y-units per pixel. This means, if scale = 10,
     41    * every physical pixel on screen are 10 x or 10 y units in the
     42    * northing/easting space of the projection.
     43    */
     44    protected double scale;
     45    /**
     46    * Center n/e coordinate of the desired screen center.
     47    */
     48    protected EastNorth center;
     49
     50    public NavigatableComponent() {
     51        setLayout(null);
     52    }
     53
     54    protected DataSet getData()
     55    {
     56        return Main.ds;
     57    }
     58
     59    /**
     60    * Return the OSM-conform zoom factor (0 for whole world, 1 for half, 2 for quarter...)
     61    */
     62    public int zoom() {
     63        double sizex = scale * getWidth();
     64        double sizey = scale * getHeight();
     65        for (int zoom = 0; zoom <= 32; zoom++, sizex *= 2, sizey *= 2)
     66            if (sizex > world.east() || sizey > world.north())
     67                return zoom;
     68        return 32;
     69    }
     70
     71    /**
     72    * Return the current scale value.
     73    * @return The scale value currently used in display
     74    */
     75    public double getScale() {
     76        return scale;
     77    }
     78
     79    /**
     80    * @return Returns the center point. A copy is returned, so users cannot
     81     *      change the center by accessing the return value. Use zoomTo instead.
     82    */
     83    public EastNorth getCenter() {
     84        return center;
     85    }
     86
     87    /**
     88    * @param x X-Pixelposition to get coordinate from
     89    * @param y Y-Pixelposition to get coordinate from
     90    *
     91    * @return Geographic coordinates from a specific pixel coordination
     92     *      on the screen.
     93    */
     94    public EastNorth getEastNorth(int x, int y) {
     95        return new EastNorth(
     96                center.east() + (x - getWidth()/2.0)*scale,
     97                center.north() - (y - getHeight()/2.0)*scale);
     98    }
     99
     100    /**
     101    * @param x X-Pixelposition to get coordinate from
     102    * @param y Y-Pixelposition to get coordinate from
     103    *
     104    * @return Geographic unprojected coordinates from a specific pixel coordination
     105     *      on the screen.
     106    */
     107    public LatLon getLatLon(int x, int y) {
     108
     109        return getProjection().eastNorth2latlon(getEastNorth(x, y));
     110    }
     111
     112    /**
     113    * Return the point on the screen where this Coordinate would be.
     114    * @param p The point, where this geopoint would be drawn.
     115    * @return The point on screen where "point" would be drawn, relative
     116     *      to the own top/left.
     117    */
     118    public Point getPoint(EastNorth p) {
     119        if(null == p)
     120            return new Point();
     121        double x = (p.east()-center.east())/scale + getWidth()/2;
     122        double y = (center.north()-p.north())/scale + getHeight()/2;
     123        return new Point((int)x,(int)y);
     124    }
     125
     126    /**
     127    * Zoom to the given coordinate.
     128    * @param newCenter The center x-value (easting) to zoom to.
     129    * @param scale The scale to use.
     130    */
     131    public void zoomTo(EastNorth newCenter, double scale) {
     132        center = newCenter;
     133        getProjection().eastNorth2latlon(center);
     134        this.scale = scale;
     135        repaint();
     136    }
     137
     138    /**
     139    * Return the nearest point to the screen point given.
     140    * If a node within 10 pixel is found, the nearest node is returned.
     141    */
     142    public final Node getNearestNode(Point p) {
     143        double minDistanceSq = Double.MAX_VALUE;
     144        Node minPrimitive = null;
     145        for (Node n : getData().nodes) {
     146            if (n.deleted || n.incomplete)
     147                continue;
     148            Point sp = getPoint(n.eastNorth);
     149            double dist = p.distanceSq(sp);
     150            if (minDistanceSq > dist && dist < snapDistance) {
     151                minDistanceSq = p.distanceSq(sp);
     152                minPrimitive = n;
     153            }
     154            // prefer already selected node when multiple nodes on one point
     155            else if(minDistanceSq == dist && n.selected && !minPrimitive.selected)
     156            {
     157                minPrimitive = n;
     158            }
     159        }
     160        return minPrimitive;
     161    }
     162
     163    /**
     164    * @return all way segments within 10px of p, sorted by their
     165    * perpendicular distance.
     166     *
     167    * @param p the point for which to search the nearest segment.
     168    */
     169    public final List<WaySegment> getNearestWaySegments(Point p) {
     170        TreeMap<Double, List<WaySegment>> nearest = new TreeMap<Double, List<WaySegment>>();
     171        for (Way w : getData().ways) {
     172            if (w.deleted || w.incomplete) continue;
     173            Node lastN = null;
     174            int i = -2;
     175            for (Node n : w.nodes) {
     176                i++;
     177                if (n.deleted || n.incomplete) continue;
     178                if (lastN == null) {
     179                    lastN = n;
     180                    continue;
     181                }
     182
     183                Point A = getPoint(lastN.eastNorth);
     184                Point B = getPoint(n.eastNorth);
     185                double c = A.distanceSq(B);
     186                double a = p.distanceSq(B);
     187                double b = p.distanceSq(A);
     188                double perDist = a-(a-b+c)*(a-b+c)/4/c; // perpendicular distance squared
     189                if (perDist < snapDistance && a < c+snapDistance && b < c+snapDistance) {
     190                    if(w.selected) // prefer selected ways a little bit
     191                        perDist -= 0.00001;
     192                    List<WaySegment> l;
     193                    if (nearest.containsKey(perDist)) {
     194                        l = nearest.get(perDist);
     195                    } else {
     196                        l = new LinkedList<WaySegment>();
     197                        nearest.put(perDist, l);
     198                    }
     199                    l.add(new WaySegment(w, i));
     200                }
     201
     202                lastN = n;
     203            }
     204        }
     205        ArrayList<WaySegment> nearestList = new ArrayList<WaySegment>();
     206        for (List<WaySegment> wss : nearest.values()) {
     207            nearestList.addAll(wss);
     208        }
     209        return nearestList;
     210    }
     211
     212    /**
     213     * @return the nearest way segment to the screen point given that is not
     214    * in ignore.
     215     *
     216    * @param p the point for which to search the nearest segment.
     217    * @param ignore a collection of segments which are not to be returned.
     218    * May be null.
     219    */
     220    public final WaySegment getNearestWaySegment(Point p, Collection<WaySegment> ignore) {
     221        List<WaySegment> nearest = getNearestWaySegments(p);
     222        if (ignore != null) nearest.removeAll(ignore);
     223        return nearest.isEmpty() ? null : nearest.get(0);
     224    }
     225
     226    /**
     227    * @return the nearest way segment to the screen point given.
     228    */
     229    public final WaySegment getNearestWaySegment(Point p) {
     230        return getNearestWaySegment(p, null);
     231    }
     232
     233    /**
     234    * @return the nearest way to the screen point given.
     235    */
     236    public final Way getNearestWay(Point p) {
     237        WaySegment nearestWaySeg = getNearestWaySegment(p);
     238        return nearestWaySeg == null ? null : nearestWaySeg.way;
     239    }
     240
     241    /**
     242    * Return the object, that is nearest to the given screen point.
     243    *
     244    * First, a node will be searched. If a node within 10 pixel is found, the
     245    * nearest node is returned.
     246    *
     247    * If no node is found, search for near ways.
     248    *
     249    * If nothing is found, return <code>null</code>.
     250    *
     251    * @param p The point on screen.
     252    * @return  The primitive that is nearest to the point p.
     253    */
     254    public OsmPrimitive getNearest(Point p) {
     255        OsmPrimitive osm = getNearestNode(p);
     256        if (osm == null)
     257        {
     258            osm = getNearestWay(p);
     259        }
     260        return osm;
     261    }
     262
     263    /**
     264    * Returns a singleton of the nearest object, or else an empty collection.
     265    */
     266    public Collection<OsmPrimitive> getNearestCollection(Point p) {
     267        OsmPrimitive osm = getNearest(p);
     268        if (osm == null)
     269            return Collections.emptySet();
     270        return Collections.singleton(osm);
     271    }
     272
     273    /**
     274    * @return A list of all objects that are nearest to
     275    * the mouse.  Does a simple sequential scan on all the data.
     276    *
     277    * @return A collection of all items or <code>null</code>
     278     *      if no item under or near the point. The returned
     279     *      list is never empty.
     280    */
     281    public Collection<OsmPrimitive> getAllNearest(Point p) {
     282        Collection<OsmPrimitive> nearest = new HashSet<OsmPrimitive>();
     283            for (Way w : getData().ways) {
     284            if (w.deleted || w.incomplete) continue;
     285            Node lastN = null;
     286            for (Node n : w.nodes) {
     287                if (n.deleted || n.incomplete) continue;
     288                if (lastN == null) {
     289                    lastN = n;
     290                    continue;
     291                }
     292                Point A = getPoint(lastN.eastNorth);
     293                Point B = getPoint(n.eastNorth);
     294                double c = A.distanceSq(B);
     295                double a = p.distanceSq(B);
     296                double b = p.distanceSq(A);
     297                double perDist = a-(a-b+c)*(a-b+c)/4/c; // perpendicular distance squared
     298                if (perDist < snapDistance && a < c+snapDistance && b < c+snapDistance) {
     299                    nearest.add(w);
     300                        break;
     301                    }
     302                lastN = n;
     303                }
     304            }
     305        for (Node n : getData().nodes) {
     306            if (!n.deleted && !n.incomplete
     307                    && getPoint(n.eastNorth).distanceSq(p) < snapDistance) {
     308                nearest.add(n);
     309            }
     310        }
     311        return nearest.isEmpty() ? null : nearest;
     312    }
     313
     314    /**
     315    * @return A list of all nodes that are nearest to
     316    * the mouse.  Does a simple sequential scan on all the data.
     317    *
     318    * @return A collection of all nodes or <code>null</code>
     319     *      if no node under or near the point. The returned
     320     *      list is never empty.
     321    */
     322    public Collection<Node> getNearestNodes(Point p) {
     323        Collection<Node> nearest = new HashSet<Node>();
     324        for (Node n : getData().nodes) {
     325            if (!n.deleted && !n.incomplete
     326                    && getPoint(n.eastNorth).distanceSq(p) < snapDistance) {
     327                nearest.add(n);
     328            }
     329        }
     330        return nearest.isEmpty() ? null : nearest;
     331    }
     332
     333    /**
     334     * @return the nearest nodes to the screen point given that is not
     335    * in ignore.
     336     *
     337    * @param p the point for which to search the nearest segment.
     338    * @param ignore a collection of nodes which are not to be returned.
     339    * May be null.
     340    */
     341    public final Collection<Node> getNearestNodes(Point p, Collection<Node> ignore) {
     342        Collection<Node> nearest = getNearestNodes(p);
    343343                if (nearest == null) return null;
    344                 if (ignore != null) nearest.removeAll(ignore);
    345                 return nearest.isEmpty() ? null : nearest;
    346         }
    347 
    348         /**
    349         * @return The projection to be used in calculating stuff.
    350         */
    351         protected Projection getProjection() {
    352                 return Main.proj;
    353         }
    354 
    355         public String helpTopic() {
    356             String n = getClass().getName();
    357             return n.substring(n.lastIndexOf('.')+1);
     344        if (ignore != null) nearest.removeAll(ignore);
     345        return nearest.isEmpty() ? null : nearest;
     346    }
     347
     348    /**
     349    * @return The projection to be used in calculating stuff.
     350    */
     351    protected Projection getProjection() {
     352        return Main.proj;
     353    }
     354
     355    public String helpTopic() {
     356        String n = getClass().getName();
     357        return n.substring(n.lastIndexOf('.')+1);
    358358    }
    359359}
  • trunk/src/org/openstreetmap/josm/gui/OsmPrimitivRenderer.java

    r627 r1169  
    1717/**
    1818 * Renderer that renders the objects from an OsmPrimitive as data.
    19  * 
     19 *
    2020 * Can be used in lists and tables.
    21  * 
     21 *
    2222 * @author imi
    2323 * @author Frederik Ramm <frederik@remote.org>
     
    2525public class OsmPrimitivRenderer implements ListCellRenderer, TableCellRenderer {
    2626
    27         /**
    28         * NameVisitor provides proper names and icons for OsmPrimitives
    29         */
    30         private NameVisitor visitor = new NameVisitor();
     27    /**
     28    * NameVisitor provides proper names and icons for OsmPrimitives
     29    */
     30    private NameVisitor visitor = new NameVisitor();
    3131
    32         /**
    33          * Default list cell renderer - delegate for ListCellRenderer operation
    34          */
    35         private DefaultListCellRenderer defaultListCellRenderer = new DefaultListCellRenderer();
    36        
    37         /**
    38          * Default table cell renderer - delegate for TableCellRenderer operation
    39          */
    40         private DefaultTableCellRenderer defaultTableCellRenderer = new DefaultTableCellRenderer();
     32    /**
     33     * Default list cell renderer - delegate for ListCellRenderer operation
     34     */
     35    private DefaultListCellRenderer defaultListCellRenderer = new DefaultListCellRenderer();
    4136
    42         /**
    43          * Adapter method supporting the ListCellRenderer interface.
    44          */
    45         public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
    46                 Component def = defaultListCellRenderer.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
    47                 return renderer(def, (OsmPrimitive) value);
    48         }
     37    /**
     38     * Default table cell renderer - delegate for TableCellRenderer operation
     39     */
     40    private DefaultTableCellRenderer defaultTableCellRenderer = new DefaultTableCellRenderer();
    4941
    50         /**
    51          * Adapter method supporting the TableCellRenderer interface.
    52          */
    53         public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
    54                 Component def = defaultTableCellRenderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
    55                 return renderer(def, (OsmPrimitive) value);
    56         }
    57        
    58         /**
    59          * Internal method that stuffs information into the rendering component
    60          * provided that it's a kind of JLabel.
    61          * @param def the rendering component
    62          * @param value the OsmPrimtive to render
    63          * @return the modified rendering component
    64          */
    65         private Component renderer(Component def, OsmPrimitive value) {
    66                 if (def != null && value != null && def instanceof JLabel) {
    67                         (value).visit(visitor);
    68                         ((JLabel)def).setText(visitor.name);
    69                         ((JLabel)def).setIcon(visitor.icon);
    70                 }
    71                 return def;
    72         }
    73        
     42    /**
     43     * Adapter method supporting the ListCellRenderer interface.
     44     */
     45    public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
     46        Component def = defaultListCellRenderer.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
     47        return renderer(def, (OsmPrimitive) value);
     48    }
     49
     50    /**
     51     * Adapter method supporting the TableCellRenderer interface.
     52     */
     53    public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
     54        Component def = defaultTableCellRenderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
     55        return renderer(def, (OsmPrimitive) value);
     56    }
     57
     58    /**
     59     * Internal method that stuffs information into the rendering component
     60     * provided that it's a kind of JLabel.
     61     * @param def the rendering component
     62     * @param value the OsmPrimtive to render
     63     * @return the modified rendering component
     64     */
     65    private Component renderer(Component def, OsmPrimitive value) {
     66        if (def != null && value != null && def instanceof JLabel) {
     67            (value).visit(visitor);
     68            ((JLabel)def).setText(visitor.name);
     69            ((JLabel)def).setIcon(visitor.icon);
     70        }
     71        return def;
     72    }
     73
    7474}
  • trunk/src/org/openstreetmap/josm/gui/PleaseWaitDialog.java

    r1073 r1169  
    2020public class PleaseWaitDialog extends JDialog {
    2121
    22         private final JProgressBar progressBar = new JProgressBar();
     22    private final JProgressBar progressBar = new JProgressBar();
    2323
    24         public final JLabel currentAction = new JLabel(I18n.tr("Contacting the OSM server..."));
    25         public final BoundedRangeModel progress = progressBar.getModel();
    26         public final JButton cancel = new JButton(I18n.tr("Cancel"));
     24    public final JLabel currentAction = new JLabel(I18n.tr("Contacting the OSM server..."));
     25    public final BoundedRangeModel progress = progressBar.getModel();
     26    public final JButton cancel = new JButton(I18n.tr("Cancel"));
    2727
    28         public PleaseWaitDialog(Component parent) {
    29                 super(JOptionPane.getFrameForComponent(parent), true);
    30                 setLayout(new GridBagLayout());
    31                 JPanel pane = new JPanel(new GridBagLayout());
    32                 pane.setBorder(BorderFactory.createEmptyBorder(10,10,10,10));
    33                 pane.add(currentAction, GBC.eol().fill(GBC.HORIZONTAL));
    34                 pane.add(progressBar, GBC.eop().fill(GBC.HORIZONTAL));
    35                 pane.add(cancel, GBC.eol().anchor(GBC.CENTER));
    36                 setContentPane(pane);
    37                 setSize(Main.pref.getInteger("progressdialog.size",400),100);
    38                 setLocationRelativeTo(Main.parent);
    39         }
     28    public PleaseWaitDialog(Component parent) {
     29        super(JOptionPane.getFrameForComponent(parent), true);
     30        setLayout(new GridBagLayout());
     31        JPanel pane = new JPanel(new GridBagLayout());
     32        pane.setBorder(BorderFactory.createEmptyBorder(10,10,10,10));
     33        pane.add(currentAction, GBC.eol().fill(GBC.HORIZONTAL));
     34        pane.add(progressBar, GBC.eop().fill(GBC.HORIZONTAL));
     35        pane.add(cancel, GBC.eol().anchor(GBC.CENTER));
     36        setContentPane(pane);
     37        setSize(Main.pref.getInteger("progressdialog.size",400),100);
     38        setLocationRelativeTo(Main.parent);
     39    }
    4040}
  • trunk/src/org/openstreetmap/josm/gui/PleaseWaitRunnable.java

    r627 r1169  
    2626public abstract class PleaseWaitRunnable implements Runnable {
    2727
    28         public String errorMessage;
     28    public String errorMessage;
    2929
    30         private boolean closeDialogCalled = false;
    31         private boolean cancelled = false;
     30    private boolean closeDialogCalled = false;
     31    private boolean cancelled = false;
    3232
    33         private final String title;
     33    private final String title;
    3434
    35         /**
    36         * Create the runnable object with a given message for the user.
    37         */
    38         public PleaseWaitRunnable(String title) {
    39                 this.title = title;
    40                 Main.pleaseWaitDlg.cancel.addActionListener(new ActionListener(){
    41                         public void actionPerformed(ActionEvent e) {
    42                                 if (!cancelled) {
    43                                         cancelled = true;
    44                                         cancel();
    45                                 }
    46                         }
    47                 });
    48                 Main.pleaseWaitDlg.addWindowListener(new WindowAdapter(){
    49                         @Override public void windowClosing(WindowEvent e) {
    50                                 if (!closeDialogCalled) {
    51                                         if (!cancelled) {
    52                                                 cancelled = true;
    53                                                 cancel();
    54                                         }
    55                                         closeDialog();
    56                                 }
    57                         }
    58                 });
    59         }
     35    /**
     36    * Create the runnable object with a given message for the user.
     37    */
     38    public PleaseWaitRunnable(String title) {
     39        this.title = title;
     40        Main.pleaseWaitDlg.cancel.addActionListener(new ActionListener(){
     41            public void actionPerformed(ActionEvent e) {
     42                if (!cancelled) {
     43                    cancelled = true;
     44                    cancel();
     45                }
     46            }
     47        });
     48        Main.pleaseWaitDlg.addWindowListener(new WindowAdapter(){
     49            @Override public void windowClosing(WindowEvent e) {
     50                if (!closeDialogCalled) {
     51                    if (!cancelled) {
     52                        cancelled = true;
     53                        cancel();
     54                    }
     55                    closeDialog();
     56                }
     57            }
     58        });
     59    }
    6060
    61         public final void run() {
    62                 try {
    63                         if (cancelled)
    64                                 return; // since realRun isn't executed, do not call to finish
     61    public final void run() {
     62        try {
     63            if (cancelled)
     64                return; // since realRun isn't executed, do not call to finish
    6565
    66                         // reset dialog state
    67                         Main.pleaseWaitDlg.setTitle(title);
    68                         errorMessage = null;
    69                         closeDialogCalled = false;
     66            // reset dialog state
     67            Main.pleaseWaitDlg.setTitle(title);
     68            errorMessage = null;
     69            closeDialogCalled = false;
    7070
    71                         // show the dialog
    72                         synchronized (this) {
    73                     EventQueue.invokeLater(new Runnable() {
    74                         public void run() {
    75                                 synchronized (PleaseWaitRunnable.this) {
    76                                         PleaseWaitRunnable.this.notifyAll();
    77                                 }
    78                                 Main.pleaseWaitDlg.setVisible(true);
    79                         }
    80                     });
    81                     try {wait();} catch (InterruptedException e) {}
    82                         }
     71            // show the dialog
     72            synchronized (this) {
     73                EventQueue.invokeLater(new Runnable() {
     74                    public void run() {
     75                        synchronized (PleaseWaitRunnable.this) {
     76                            PleaseWaitRunnable.this.notifyAll();
     77                        }
     78                        Main.pleaseWaitDlg.setVisible(true);
     79                    }
     80                });
     81                try {wait();} catch (InterruptedException e) {}
     82            }
    8383
    84                         realRun();
    85                 } catch (SAXException x) {
    86                         x.printStackTrace();
    87                         errorMessage = tr("Error while parsing")+": "+x.getMessage();
    88                 } catch (FileNotFoundException x) {
    89                         x.printStackTrace();
    90                         errorMessage = tr("File not found")+": "+x.getMessage();
    91                 } catch (IOException x) {
    92                         x.printStackTrace();
    93                         errorMessage = x.getMessage();
    94                 } finally {
    95                         closeDialog();
    96                 }
    97         }
     84            realRun();
     85        } catch (SAXException x) {
     86            x.printStackTrace();
     87            errorMessage = tr("Error while parsing")+": "+x.getMessage();
     88        } catch (FileNotFoundException x) {
     89            x.printStackTrace();
     90            errorMessage = tr("File not found")+": "+x.getMessage();
     91        } catch (IOException x) {
     92            x.printStackTrace();
     93            errorMessage = x.getMessage();
     94        } finally {
     95            closeDialog();
     96        }
     97    }
    9898
    99         /**
    100         * User pressed cancel button.
    101         */
    102         protected abstract void cancel();
     99    /**
     100    * User pressed cancel button.
     101    */
     102    protected abstract void cancel();
    103103
    104         /**
    105         * Called in the worker thread to do the actual work. When any of the
    106         * exception is thrown, a message box will be displayed and closeDialog
    107         * is called. finish() is called in any case.
    108         */
    109         protected abstract void realRun() throws SAXException, IOException;
     104    /**
     105    * Called in the worker thread to do the actual work. When any of the
     106    * exception is thrown, a message box will be displayed and closeDialog
     107    * is called. finish() is called in any case.
     108    */
     109    protected abstract void realRun() throws SAXException, IOException;
    110110
    111         /**
    112         * Finish up the data work. Is guaranteed to be called if realRun is called.
    113         * Finish is called in the gui thread just after the dialog disappeared.
    114         */
    115         protected abstract void finish();
     111    /**
     112    * Finish up the data work. Is guaranteed to be called if realRun is called.
     113    * Finish is called in the gui thread just after the dialog disappeared.
     114    */
     115    protected abstract void finish();
    116116
    117         /**
    118         * Close the dialog. Usually called from worker thread.
    119         */
    120         public void closeDialog() {
    121                 if (closeDialogCalled)
    122                         return;
    123                 closeDialogCalled  = true;
    124                 try {
    125                         Runnable runnable = new Runnable(){
    126                                 public void run() {
    127                                         try {
    128                                                 finish();
    129                                         } finally {
    130                                                 Main.pleaseWaitDlg.setVisible(false);
    131                                                 Main.pleaseWaitDlg.dispose();
    132                                         }
    133                                         if (errorMessage != null)
    134                                                 JOptionPane.showMessageDialog(Main.parent, errorMessage);
    135                                 }
    136                         };
     117    /**
     118    * Close the dialog. Usually called from worker thread.
     119    */
     120    public void closeDialog() {
     121        if (closeDialogCalled)
     122            return;
     123        closeDialogCalled  = true;
     124        try {
     125            Runnable runnable = new Runnable(){
     126                public void run() {
     127                    try {
     128                        finish();
     129                    } finally {
     130                        Main.pleaseWaitDlg.setVisible(false);
     131                        Main.pleaseWaitDlg.dispose();
     132                    }
     133                    if (errorMessage != null)
     134                        JOptionPane.showMessageDialog(Main.parent, errorMessage);
     135                }
     136            };
    137137
    138                         // make sure, this is called in the dispatcher thread ASAP
    139                         if (EventQueue.isDispatchThread())
    140                                 runnable.run();
    141                         else
    142                                 EventQueue.invokeAndWait(runnable);
     138            // make sure, this is called in the dispatcher thread ASAP
     139            if (EventQueue.isDispatchThread())
     140                runnable.run();
     141            else
     142                EventQueue.invokeAndWait(runnable);
    143143
    144                 } catch (InterruptedException e) {
    145                 } catch (InvocationTargetException e) {
    146                         throw new RuntimeException(e);
    147                 }
    148         }
     144        } catch (InterruptedException e) {
     145        } catch (InvocationTargetException e) {
     146            throw new RuntimeException(e);
     147        }
     148    }
    149149}
  • trunk/src/org/openstreetmap/josm/gui/QuadStateCheckBox.java

    r747 r1169  
    2323public class QuadStateCheckBox extends JCheckBox {
    2424
    25         public enum State { NOT_SELECTED, SELECTED, UNSET, PARTIAL }
     25    public enum State { NOT_SELECTED, SELECTED, UNSET, PARTIAL }
    2626
    27         private final QuadStateDecorator model;
    28         private State[] allowed;
     27    private final QuadStateDecorator model;
     28    private State[] allowed;
    2929
    30         public QuadStateCheckBox(String text, Icon icon, State initial, State[] allowed) {
    31                 super(text, icon);
    32                 this.allowed = allowed;
    33                 // Add a listener for when the mouse is pressed
    34                 super.addMouseListener(new MouseAdapter() {
    35                         @Override public void mousePressed(MouseEvent e) {
    36                                 grabFocus();
    37                                 model.nextState();
    38                         }
    39                 });
    40                 // Reset the keyboard action map
    41                 ActionMap map = new ActionMapUIResource();
    42                 map.put("pressed", new AbstractAction() {
    43                         public void actionPerformed(ActionEvent e) {
    44                                 grabFocus();
    45                                 model.nextState();
    46                         }
    47                 });
    48                 map.put("released", null);
    49                 SwingUtilities.replaceUIActionMap(this, map);
    50                 // set the model to the adapted model
    51                 model = new QuadStateDecorator(getModel());
    52                 setModel(model);
    53                 setState(initial);
    54         }
    55         public QuadStateCheckBox(String text, State initial, State[] allowed) {
    56                 this(text, null, initial, allowed);
    57         }
     30    public QuadStateCheckBox(String text, Icon icon, State initial, State[] allowed) {
     31        super(text, icon);
     32        this.allowed = allowed;
     33        // Add a listener for when the mouse is pressed
     34        super.addMouseListener(new MouseAdapter() {
     35            @Override public void mousePressed(MouseEvent e) {
     36                grabFocus();
     37                model.nextState();
     38            }
     39        });
     40        // Reset the keyboard action map
     41        ActionMap map = new ActionMapUIResource();
     42        map.put("pressed", new AbstractAction() {
     43            public void actionPerformed(ActionEvent e) {
     44                grabFocus();
     45                model.nextState();
     46            }
     47        });
     48        map.put("released", null);
     49        SwingUtilities.replaceUIActionMap(this, map);
     50        // set the model to the adapted model
     51        model = new QuadStateDecorator(getModel());
     52        setModel(model);
     53        setState(initial);
     54    }
     55    public QuadStateCheckBox(String text, State initial, State[] allowed) {
     56        this(text, null, initial, allowed);
     57    }
    5858
    59         /** Do not let anyone add mouse listeners */
    60         @Override public void addMouseListener(MouseListener l) { }
    61         /**
    62         * Set the new state.
    63         */
    64         public void setState(State state) { model.setState(state); }
    65         /** Return the current state, which is determined by the
    66         * selection status of the model. */
    67         public State getState() { return model.getState(); }
    68         @Override public void setSelected(boolean b) {
    69                 if (b) {
    70                         setState(State.SELECTED);
    71                 } else {
    72                         setState(State.NOT_SELECTED);
    73                 }
    74         }
     59    /** Do not let anyone add mouse listeners */
     60    @Override public void addMouseListener(MouseListener l) { }
     61    /**
     62    * Set the new state.
     63    */
     64    public void setState(State state) { model.setState(state); }
     65    /** Return the current state, which is determined by the
     66    * selection status of the model. */
     67    public State getState() { return model.getState(); }
     68    @Override public void setSelected(boolean b) {
     69        if (b) {
     70            setState(State.SELECTED);
     71        } else {
     72            setState(State.NOT_SELECTED);
     73        }
     74    }
    7575
    76         private class QuadStateDecorator implements ButtonModel {
    77                 private final ButtonModel other;
    78                 private QuadStateDecorator(ButtonModel other) {
    79                         this.other = other;
    80                 }
    81                 private void setState(State state) {
    82                         if (state == State.NOT_SELECTED) {
    83                                 other.setArmed(false);
    84                                 other.setPressed(false);
    85                                 other.setSelected(false);
    86                                 setToolTipText(tr("false: the property is explicitly switched off"));
    87                         } else if (state == State.SELECTED) {
    88                                 other.setArmed(false);
    89                                 other.setPressed(false);
    90                                 other.setSelected(true);
    91                                 setToolTipText(tr("true: the property is explicitly switched on"));
    92                         } else if (state == State.PARTIAL) {
    93                                 other.setArmed(true);
    94                                 other.setPressed(true);
    95                                 other.setSelected(true);
    96                                 setToolTipText(tr("partial: different selected objects have different values, do not change"));
    97                         } else {
    98                                 other.setArmed(true);
    99                                 other.setPressed(true);
    100                                 other.setSelected(false);
    101                                 setToolTipText(tr("unset: do not set this property on the selected objects"));
    102                         }
    103                 }
    104                 /**
    105                 * The current state is embedded in the selection / armed
    106                 * state of the model.
    107                  *
    108                 * We return the SELECTED state when the checkbox is selected
    109                 * but not armed, PARTIAL state when the checkbox is
    110                 * selected and armed (grey) and NOT_SELECTED when the
    111                 * checkbox is deselected.
    112                 */
    113                 private State getState() {
    114                         if (isSelected() && !isArmed()) {
    115                                 // normal black tick
    116                                 return State.SELECTED;
    117                         } else if (isSelected() && isArmed()) {
    118                                 // don't care grey tick
    119                                 return State.PARTIAL;
    120                         } else if (!isSelected() && !isArmed()) {
    121                                 return State.NOT_SELECTED;
    122                         } else {
    123                                 return State.UNSET;
    124                         }
    125                 }
    126                 /** Rotate to the next allowed state.*/
    127                 private void nextState() {
    128                         State current = getState();
    129                         for (int i = 0; i < allowed.length; i++) {
    130                                 if (allowed[i] == current) {
    131                                         setState((i == allowed.length-1) ? allowed[0] : allowed[i+1]);
    132                                         break;
    133                                 }
    134                         }
    135                 }
    136                 /** Filter: No one may change the armed/selected/pressed status except us. */
    137                 public void setArmed(boolean b) { }
    138                 public void setSelected(boolean b) { }
    139                 public void setPressed(boolean b) { }
    140                 /** We disable focusing on the component when it is not
    141                 * enabled. */
    142                 public void setEnabled(boolean b) {
    143                         setFocusable(b);
    144                         other.setEnabled(b);
    145                 }
    146                 /** All these methods simply delegate to the "other" model
    147                 * that is being decorated. */
    148                 public boolean isArmed() { return other.isArmed(); }
    149                 public boolean isSelected() { return other.isSelected(); }
    150                 public boolean isEnabled() { return other.isEnabled(); }
    151                 public boolean isPressed() { return other.isPressed(); }
    152                 public boolean isRollover() { return other.isRollover(); }
    153                 public void setRollover(boolean b) { other.setRollover(b); }
    154                 public void setMnemonic(int key) { other.setMnemonic(key); }
    155                 public int getMnemonic() { return other.getMnemonic(); }
    156                 public void setActionCommand(String s) {
    157                         other.setActionCommand(s);
    158                 }
    159                 public String getActionCommand() {
    160                         return other.getActionCommand();
    161                 }
    162                 public void setGroup(ButtonGroup group) {
    163                         other.setGroup(group);
    164                 }
    165                 public void addActionListener(ActionListener l) {
    166                         other.addActionListener(l);
    167                 }
    168                 public void removeActionListener(ActionListener l) {
    169                         other.removeActionListener(l);
    170                 }
    171                 public void addItemListener(ItemListener l) {
    172                         other.addItemListener(l);
    173                 }
    174                 public void removeItemListener(ItemListener l) {
    175                         other.removeItemListener(l);
    176                 }
    177                 public void addChangeListener(ChangeListener l) {
    178                         other.addChangeListener(l);
    179                 }
    180                 public void removeChangeListener(ChangeListener l) {
    181                         other.removeChangeListener(l);
    182                 }
    183                 public Object[] getSelectedObjects() {
    184                         return other.getSelectedObjects();
    185                 }
    186         }
     76    private class QuadStateDecorator implements ButtonModel {
     77        private final ButtonModel other;
     78        private QuadStateDecorator(ButtonModel other) {
     79            this.other = other;
     80        }
     81        private void setState(State state) {
     82            if (state == State.NOT_SELECTED) {
     83                other.setArmed(false);
     84                other.setPressed(false);
     85                other.setSelected(false);
     86                setToolTipText(tr("false: the property is explicitly switched off"));
     87            } else if (state == State.SELECTED) {
     88                other.setArmed(false);
     89                other.setPressed(false);
     90                other.setSelected(true);
     91                setToolTipText(tr("true: the property is explicitly switched on"));
     92            } else if (state == State.PARTIAL) {
     93                other.setArmed(true);
     94                other.setPressed(true);
     95                other.setSelected(true);
     96                setToolTipText(tr("partial: different selected objects have different values, do not change"));
     97            } else {
     98                other.setArmed(true);
     99                other.setPressed(true);
     100                other.setSelected(false);
     101                setToolTipText(tr("unset: do not set this property on the selected objects"));
     102            }
     103        }
     104        /**
     105        * The current state is embedded in the selection / armed
     106        * state of the model.
     107         *
     108        * We return the SELECTED state when the checkbox is selected
     109        * but not armed, PARTIAL state when the checkbox is
     110        * selected and armed (grey) and NOT_SELECTED when the
     111        * checkbox is deselected.
     112        */
     113        private State getState() {
     114            if (isSelected() && !isArmed()) {
     115                // normal black tick
     116                return State.SELECTED;
     117            } else if (isSelected() && isArmed()) {
     118                // don't care grey tick
     119                return State.PARTIAL;
     120            } else if (!isSelected() && !isArmed()) {
     121                return State.NOT_SELECTED;
     122            } else {
     123                return State.UNSET;
     124            }
     125        }
     126        /** Rotate to the next allowed state.*/
     127        private void nextState() {
     128            State current = getState();
     129            for (int i = 0; i < allowed.length; i++) {
     130                if (allowed[i] == current) {
     131                    setState((i == allowed.length-1) ? allowed[0] : allowed[i+1]);
     132                    break;
     133                }
     134            }
     135        }
     136        /** Filter: No one may change the armed/selected/pressed status except us. */
     137        public void setArmed(boolean b) { }
     138        public void setSelected(boolean b) { }
     139        public void setPressed(boolean b) { }
     140        /** We disable focusing on the component when it is not
     141        * enabled. */
     142        public void setEnabled(boolean b) {
     143            setFocusable(b);
     144            other.setEnabled(b);
     145        }
     146        /** All these methods simply delegate to the "other" model
     147        * that is being decorated. */
     148        public boolean isArmed() { return other.isArmed(); }
     149        public boolean isSelected() { return other.isSelected(); }
     150        public boolean isEnabled() { return other.isEnabled(); }
     151        public boolean isPressed() { return other.isPressed(); }
     152        public boolean isRollover() { return other.isRollover(); }
     153        public void setRollover(boolean b) { other.setRollover(b); }
     154        public void setMnemonic(int key) { other.setMnemonic(key); }
     155        public int getMnemonic() { return other.getMnemonic(); }
     156        public void setActionCommand(String s) {
     157            other.setActionCommand(s);
     158        }
     159        public String getActionCommand() {
     160            return other.getActionCommand();
     161        }
     162        public void setGroup(ButtonGroup group) {
     163            other.setGroup(group);
     164        }
     165        public void addActionListener(ActionListener l) {
     166            other.addActionListener(l);
     167        }
     168        public void removeActionListener(ActionListener l) {
     169            other.removeActionListener(l);
     170        }
     171        public void addItemListener(ItemListener l) {
     172            other.addItemListener(l);
     173        }
     174        public void removeItemListener(ItemListener l) {
     175            other.removeItemListener(l);
     176        }
     177        public void addChangeListener(ChangeListener l) {
     178            other.addChangeListener(l);
     179        }
     180        public void removeChangeListener(ChangeListener l) {
     181            other.removeChangeListener(l);
     182        }
     183        public Object[] getSelectedObjects() {
     184            return other.getSelectedObjects();
     185        }
     186    }
    187187}
    188188
  • trunk/src/org/openstreetmap/josm/gui/SelectionManager.java

    r1003 r1169  
    2323 * Manages the selection of a rectangle. Listening to left and right mouse button
    2424 * presses and to mouse motions and draw the rectangle accordingly.
    25  * 
     25 *
    2626 * Left mouse button selects a rectangle from the press until release. Pressing
    2727 * right mouse button while left is still pressed enable the rectangle to move
     
    2929 * at constructor, except if the right is still pressed, which just remove the
    3030 * selection rectangle and does nothing.
    31  * 
    32  * The point where the left mouse button was pressed and the current mouse 
     31 *
     32 * The point where the left mouse button was pressed and the current mouse
    3333 * position are two opposite corners of the selection rectangle.
    34  * 
    35  * It is possible to specify an aspect ratio (width per height) which the 
     34 *
     35 * It is possible to specify an aspect ratio (width per height) which the
    3636 * selection rectangle always must have. In this case, the selection rectangle
    3737 * will be the largest window with this aspect ratio, where the position the left
    38  * mouse button was pressed and the corner of the current mouse position are at 
     38 * mouse button was pressed and the corner of the current mouse position are at
    3939 * opposite sites (the mouse position corner is the corner nearest to the mouse
    40  * cursor). 
    41  * 
    42  * When the left mouse button was released, an ActionEvent is send to the 
     40 * cursor).
     41 *
     42 * When the left mouse button was released, an ActionEvent is send to the
    4343 * ActionListener given at constructor. The source of this event is this manager.
    44  * 
     44 *
    4545 * @author imi
    4646 */
    4747public class SelectionManager implements MouseListener, MouseMotionListener, PropertyChangeListener {
    4848
    49         /**
    50         * This is the interface that an user of SelectionManager has to implement
    51         * to get informed when a selection closes.
    52         * @author imi
    53         */
    54         public interface SelectionEnded {
    55                 /**
    56                 * Called, when the left mouse button was released.
    57                 * @param r The rectangle that is currently the selection.
    58                 * @param alt Whether the alt key was pressed
    59                 * @param shift Whether the shift key was pressed
    60                  * @param ctrl Whether the ctrl key was pressed
    61                 * @see InputEvent#getModifiersEx()
    62                 */
    63                 public void selectionEnded(Rectangle r, boolean alt, boolean shift, boolean ctrl);
    64                 /**
    65                 * Called to register the selection manager for "active" property.
    66                 * @param listener The listener to register
    67                 */
    68                 public void addPropertyChangeListener(PropertyChangeListener listener);
    69                 /**
    70                  * Called to remove the selection manager from the listener list
    71                 * for "active" property.
    72                 * @param listener The listener to register
    73                 */
    74                 public void removePropertyChangeListener(PropertyChangeListener listener);
    75         }
    76         /**
    77         * The listener that receives the events after left mouse button is released.
    78         */
    79         private final SelectionEnded selectionEndedListener;
    80         /**
    81         * Position of the map when the mouse button was pressed.
    82          * If this is not <code>null</code>, a rectangle is drawn on screen.
    83         */
    84         private Point mousePosStart;
    85         /**
    86         * Position of the map when the selection rectangle was last drawn.
    87         */
    88         private Point mousePos;
    89         /**
    90         * The Component, the selection rectangle is drawn onto.
    91         */
    92         private final NavigatableComponent nc;
    93         /**
    94          * Whether the selection rectangle must obtain the aspect ratio of the
    95         * drawComponent.
    96         */
    97         private boolean aspectRatio;
    98 
    99         /**
    100         * Create a new SelectionManager.
    101         *
    102         * @param selectionEndedListener The action listener that receives the event when
    103          *              the left button is released.
    104         * @param aspectRatio If true, the selection window must obtain the aspect
    105          *              ratio of the drawComponent.
    106         * @param navComp The component, the rectangle is drawn onto.
    107         */
    108         public SelectionManager(SelectionEnded selectionEndedListener, boolean aspectRatio, NavigatableComponent navComp) {
    109                 this.selectionEndedListener = selectionEndedListener;
    110                 this.aspectRatio = aspectRatio;
    111                 this.nc = navComp;
    112         }
    113        
    114         /**
    115         * Register itself at the given event source.
    116         * @param eventSource The emitter of the mouse events.
    117         */
    118         public void register(NavigatableComponent eventSource) {
    119                 eventSource.addMouseListener(this);
    120                 eventSource.addMouseMotionListener(this);
    121                 selectionEndedListener.addPropertyChangeListener(this);
     49    /**
     50    * This is the interface that an user of SelectionManager has to implement
     51    * to get informed when a selection closes.
     52    * @author imi
     53    */
     54    public interface SelectionEnded {
     55        /**
     56        * Called, when the left mouse button was released.
     57        * @param r The rectangle that is currently the selection.
     58        * @param alt Whether the alt key was pressed
     59        * @param shift Whether the shift key was pressed
     60         * @param ctrl Whether the ctrl key was pressed
     61        * @see InputEvent#getModifiersEx()
     62        */
     63        public void selectionEnded(Rectangle r, boolean alt, boolean shift, boolean ctrl);
     64        /**
     65        * Called to register the selection manager for "active" property.
     66        * @param listener The listener to register
     67        */
     68        public void addPropertyChangeListener(PropertyChangeListener listener);
     69        /**
     70         * Called to remove the selection manager from the listener list
     71        * for "active" property.
     72        * @param listener The listener to register
     73        */
     74        public void removePropertyChangeListener(PropertyChangeListener listener);
     75    }
     76    /**
     77    * The listener that receives the events after left mouse button is released.
     78    */
     79    private final SelectionEnded selectionEndedListener;
     80    /**
     81    * Position of the map when the mouse button was pressed.
     82     * If this is not <code>null</code>, a rectangle is drawn on screen.
     83    */
     84    private Point mousePosStart;
     85    /**
     86    * Position of the map when the selection rectangle was last drawn.
     87    */
     88    private Point mousePos;
     89    /**
     90    * The Component, the selection rectangle is drawn onto.
     91    */
     92    private final NavigatableComponent nc;
     93    /**
     94     * Whether the selection rectangle must obtain the aspect ratio of the
     95    * drawComponent.
     96    */
     97    private boolean aspectRatio;
     98
     99    /**
     100    * Create a new SelectionManager.
     101    *
     102    * @param selectionEndedListener The action listener that receives the event when
     103     *      the left button is released.
     104    * @param aspectRatio If true, the selection window must obtain the aspect
     105     *      ratio of the drawComponent.
     106    * @param navComp The component, the rectangle is drawn onto.
     107    */
     108    public SelectionManager(SelectionEnded selectionEndedListener, boolean aspectRatio, NavigatableComponent navComp) {
     109        this.selectionEndedListener = selectionEndedListener;
     110        this.aspectRatio = aspectRatio;
     111        this.nc = navComp;
     112    }
     113
     114    /**
     115    * Register itself at the given event source.
     116    * @param eventSource The emitter of the mouse events.
     117    */
     118    public void register(NavigatableComponent eventSource) {
     119        eventSource.addMouseListener(this);
     120        eventSource.addMouseMotionListener(this);
     121        selectionEndedListener.addPropertyChangeListener(this);
    122122        eventSource.addPropertyChangeListener("scale", new PropertyChangeListener(){
    123                         public void propertyChange(PropertyChangeEvent evt) {
    124                                 if (mousePosStart != null) {
    125                                         paintRect();
    126                                         mousePos = mousePosStart = null;
    127                                 }
     123            public void propertyChange(PropertyChangeEvent evt) {
     124                if (mousePosStart != null) {
     125                    paintRect();
     126                    mousePos = mousePosStart = null;
     127                }
    128128            }
    129129        });
    130         }
    131         /**
    132         * Unregister itself from the given event source. If a selection rectangle is
    133         * shown, hide it first.
    134         *
    135         * @param eventSource The emitter of the mouse events.
    136         */
    137         public void unregister(Component eventSource) {
    138                 eventSource.removeMouseListener(this);
    139                 eventSource.removeMouseMotionListener(this);
    140                 selectionEndedListener.removePropertyChangeListener(this);
    141         }
    142 
    143         /**
    144         * If the correct button, from the "drawing rectangle" mode
    145         */
    146         public void mousePressed(MouseEvent e) {
    147                 if (e.getButton() == MouseEvent.BUTTON1)
    148                         mousePosStart = mousePos = e.getPoint();
    149         }
    150 
    151         /**
    152         * If the correct button is hold, draw the rectangle.
    153         */
    154         public void mouseDragged(MouseEvent e) {
    155                 int buttonPressed = e.getModifiersEx() & (MouseEvent.BUTTON1_DOWN_MASK | MouseEvent.BUTTON3_DOWN_MASK);
    156 
    157                
    158                 if (buttonPressed != 0) {
    159                         if (mousePosStart == null)
    160                                 mousePosStart = mousePos = e.getPoint();
    161                         paintRect();
    162                 }
    163                
    164                 if (buttonPressed == MouseEvent.BUTTON1_DOWN_MASK) {
    165                         mousePos = e.getPoint();
    166                         paintRect();
    167                 } else if (buttonPressed == (MouseEvent.BUTTON1_DOWN_MASK | MouseEvent.BUTTON3_DOWN_MASK)) {
    168                         mousePosStart.x += e.getX()-mousePos.x;
    169                         mousePosStart.y += e.getY()-mousePos.y;
    170                         mousePos = e.getPoint();
    171                         paintRect();
    172                 }
    173         }
    174 
    175         /**
    176         * Check the state of the keys and buttons and set the selection accordingly.
    177         */
    178         public void mouseReleased(MouseEvent e) {
    179                 if (e.getButton() != MouseEvent.BUTTON1)
    180                         return;
    181                 if (mousePos == null || mousePosStart == null)
    182                         return; // injected release from outside
    183                        
    184                 // disable the selection rect
    185                 paintRect();
    186                 Rectangle r = getSelectionRectangle();
    187                 mousePosStart = null;
    188                 mousePos = null;
    189 
    190                 boolean shift = (e.getModifiersEx() & MouseEvent.SHIFT_DOWN_MASK) != 0;
    191                 boolean alt = (e.getModifiersEx() & MouseEvent.ALT_DOWN_MASK) != 0;
    192                 boolean ctrl = (e.getModifiersEx() & MouseEvent.CTRL_DOWN_MASK) != 0;
    193                 if ((e.getModifiersEx() & MouseEvent.BUTTON3_DOWN_MASK) == 0)
    194                         selectionEndedListener.selectionEnded(r, alt, shift, ctrl);
    195         }
    196 
    197 
    198         /**
    199         * Draw a selection rectangle on screen. If already a rectangle is drawn,
    200         * it is removed instead.
    201         */
    202         private void paintRect() {
    203                 if (mousePos == null || mousePosStart == null || mousePos == mousePosStart)
    204                         return;
    205                 Graphics g = nc.getGraphics();
    206                 g.setColor(Color.BLACK);
    207                 g.setXORMode(Color.WHITE);
    208 
    209                 Rectangle r = getSelectionRectangle();
    210                 g.drawRect(r.x,r.y,r.width,r.height);
    211         }
    212 
    213         /**
    214         * Calculate and return the current selection rectangle
    215         * @return A rectangle that spans from mousePos to mouseStartPos
    216         */
    217         private Rectangle getSelectionRectangle() {
    218                 int x = mousePosStart.x;
    219                 int y = mousePosStart.y;
    220                 int w = mousePos.x - mousePosStart.x;
    221                 int h = mousePos.y - mousePosStart.y;
    222                 if (w < 0) {
    223                         x += w;
    224                         w = -w;
    225                 }
    226                 if (h < 0) {
    227                         y += h;
    228                         h = -h;
    229                 }
    230                
    231                 if (aspectRatio) {
    232                         /* Keep the aspect ratio by growing the rectangle; the
    233                         * rectangle is always under the cursor. */
    234                         double aspectRatio = (double)nc.getWidth()/nc.getHeight();
    235                         if ((double)w/h < aspectRatio) {
    236                                 int neww = (int)(h*aspectRatio);
    237                                 if (mousePos.x < mousePosStart.x)
    238                                         x += w - neww;
    239                                 w = neww;
    240                         } else {
    241                                 int newh = (int)(w/aspectRatio);
    242                                 if (mousePos.y < mousePosStart.y)
    243                                         y += h - newh;
    244                                 h = newh;
    245                         }
    246                 }
    247                
    248                 return new Rectangle(x,y,w,h);
    249         }
    250 
    251         /**
    252         * If the action goes inactive, remove the selection rectangle from screen
    253         */
    254         public void propertyChange(PropertyChangeEvent evt) {
    255                 if (evt.getPropertyName().equals("active") && !(Boolean)evt.getNewValue() && mousePosStart != null) {
    256                         paintRect();
    257                         mousePosStart = null;
    258                         mousePos = null;
    259                 }
    260         }
    261 
    262         /**
    263         * Return a list of all objects in the rectangle, respecting the different
    264         * modifier.
    265         * @param alt Whether the alt key was pressed, which means select all objects
    266          *              that are touched, instead those which are completly covered.
    267         */
    268         public Collection<OsmPrimitive> getObjectsInRectangle(Rectangle r, boolean alt) {
    269                 Collection<OsmPrimitive> selection = new LinkedList<OsmPrimitive>();
    270 
    271                 // whether user only clicked, not dragged.
    272                 boolean clicked = r.width <= 2 && r.height <= 2;
    273                 Point center = new Point(r.x+r.width/2, r.y+r.height/2);
    274 
    275                 if (clicked) {
    276                         OsmPrimitive osm = nc.getNearest(center);
    277                         if (osm != null)
    278                                 selection.add(osm);
    279                 } else {
    280                         // nodes
    281                         for (Node n : nc.getData().nodes) {
    282                                 if (!n.deleted && !n.incomplete && r.contains(nc.getPoint(n.eastNorth)))
    283                                         selection.add(n);
    284                         }
    285                        
    286                         // ways
    287                         for (Way w : nc.getData().ways) {
    288                                 if (w.deleted || w.nodes.isEmpty() || w.incomplete)
    289                                                 continue;
    290                                 if (alt) {
    291                                         for (Node n : w.nodes) {
    292                                                 if (!n.incomplete && r.contains(nc.getPoint(n.eastNorth))) {
    293                                                         selection.add(w);
    294                                                         break;
    295                                                 }
    296                                         }
    297                                 } else {
    298                                         boolean allIn = true;
    299                                         for (Node n : w.nodes) {
    300                                                 if (!n.incomplete && !r.contains(nc.getPoint(n.eastNorth))) {
    301                                                         allIn = false;
    302                                                         break;
    303                                                 }
    304                                         }
    305                                         if (allIn) selection.add(w);
    306                                 }
    307                         }
    308                 }
    309                 return selection;
    310         }
    311        
    312         public void mouseClicked(MouseEvent e) {}
    313         public void mouseEntered(MouseEvent e) {}
    314         public void mouseExited(MouseEvent e) {}
    315         public void mouseMoved(MouseEvent e) {}
     130    }
     131    /**
     132    * Unregister itself from the given event source. If a selection rectangle is
     133    * shown, hide it first.
     134    *
     135    * @param eventSource The emitter of the mouse events.
     136    */
     137    public void unregister(Component eventSource) {
     138        eventSource.removeMouseListener(this);
     139        eventSource.removeMouseMotionListener(this);
     140        selectionEndedListener.removePropertyChangeListener(this);
     141    }
     142
     143    /**
     144    * If the correct button, from the "drawing rectangle" mode
     145    */
     146    public void mousePressed(MouseEvent e) {
     147        if (e.getButton() == MouseEvent.BUTTON1)
     148            mousePosStart = mousePos = e.getPoint();
     149    }
     150
     151    /**
     152    * If the correct button is hold, draw the rectangle.
     153    */
     154    public void mouseDragged(MouseEvent e) {
     155        int buttonPressed = e.getModifiersEx() & (MouseEvent.BUTTON1_DOWN_MASK | MouseEvent.BUTTON3_DOWN_MASK);
     156
     157
     158        if (buttonPressed != 0) {
     159            if (mousePosStart == null)
     160                mousePosStart = mousePos = e.getPoint();
     161            paintRect();
     162        }
     163
     164        if (buttonPressed == MouseEvent.BUTTON1_DOWN_MASK) {
     165            mousePos = e.getPoint();
     166            paintRect();
     167        } else if (buttonPressed == (MouseEvent.BUTTON1_DOWN_MASK | MouseEvent.BUTTON3_DOWN_MASK)) {
     168            mousePosStart.x += e.getX()-mousePos.x;
     169            mousePosStart.y += e.getY()-mousePos.y;
     170            mousePos = e.getPoint();
     171            paintRect();
     172        }
     173    }
     174
     175    /**
     176    * Check the state of the keys and buttons and set the selection accordingly.
     177    */
     178    public void mouseReleased(MouseEvent e) {
     179        if (e.getButton() != MouseEvent.BUTTON1)
     180            return;
     181        if (mousePos == null || mousePosStart == null)
     182            return; // injected release from outside
     183
     184        // disable the selection rect
     185        paintRect();
     186        Rectangle r = getSelectionRectangle();
     187        mousePosStart = null;
     188        mousePos = null;
     189
     190        boolean shift = (e.getModifiersEx() & MouseEvent.SHIFT_DOWN_MASK) != 0;
     191        boolean alt = (e.getModifiersEx() & MouseEvent.ALT_DOWN_MASK) != 0;
     192        boolean ctrl = (e.getModifiersEx() & MouseEvent.CTRL_DOWN_MASK) != 0;
     193        if ((e.getModifiersEx() & MouseEvent.BUTTON3_DOWN_MASK) == 0)
     194            selectionEndedListener.selectionEnded(r, alt, shift, ctrl);
     195    }
     196
     197
     198    /**
     199    * Draw a selection rectangle on screen. If already a rectangle is drawn,
     200    * it is removed instead.
     201    */
     202    private void paintRect() {
     203        if (mousePos == null || mousePosStart == null || mousePos == mousePosStart)
     204            return;
     205        Graphics g = nc.getGraphics();
     206        g.setColor(Color.BLACK);
     207        g.setXORMode(Color.WHITE);
     208
     209        Rectangle r = getSelectionRectangle();
     210        g.drawRect(r.x,r.y,r.width,r.height);
     211    }
     212
     213    /**
     214    * Calculate and return the current selection rectangle
     215    * @return A rectangle that spans from mousePos to mouseStartPos
     216    */
     217    private Rectangle getSelectionRectangle() {
     218        int x = mousePosStart.x;
     219        int y = mousePosStart.y;
     220        int w = mousePos.x - mousePosStart.x;
     221        int h = mousePos.y - mousePosStart.y;
     222        if (w < 0) {
     223            x += w;
     224            w = -w;
     225        }
     226        if (h < 0) {
     227            y += h;
     228            h = -h;
     229        }
     230
     231        if (aspectRatio) {
     232            /* Keep the aspect ratio by growing the rectangle; the
     233            * rectangle is always under the cursor. */
     234            double aspectRatio = (double)nc.getWidth()/nc.getHeight();
     235            if ((double)w/h < aspectRatio) {
     236                int neww = (int)(h*aspectRatio);
     237                if (mousePos.x < mousePosStart.x)
     238                    x += w - neww;
     239                w = neww;
     240            } else {
     241                int newh = (int)(w/aspectRatio);
     242                if (mousePos.y < mousePosStart.y)
     243                    y += h - newh;
     244                h = newh;
     245            }
     246        }
     247
     248        return new Rectangle(x,y,w,h);
     249    }
     250
     251    /**
     252    * If the action goes inactive, remove the selection rectangle from screen
     253    */
     254    public void propertyChange(PropertyChangeEvent evt) {
     255        if (evt.getPropertyName().equals("active") && !(Boolean)evt.getNewValue() && mousePosStart != null) {
     256            paintRect();
     257            mousePosStart = null;
     258            mousePos = null;
     259        }
     260    }
     261
     262    /**
     263    * Return a list of all objects in the rectangle, respecting the different
     264    * modifier.
     265    * @param alt Whether the alt key was pressed, which means select all objects
     266     *      that are touched, instead those which are completly covered.
     267    */
     268    public Collection<OsmPrimitive> getObjectsInRectangle(Rectangle r, boolean alt) {
     269        Collection<OsmPrimitive> selection = new LinkedList<OsmPrimitive>();
     270
     271        // whether user only clicked, not dragged.
     272        boolean clicked = r.width <= 2 && r.height <= 2;
     273        Point center = new Point(r.x+r.width/2, r.y+r.height/2);
     274
     275        if (clicked) {
     276            OsmPrimitive osm = nc.getNearest(center);
     277            if (osm != null)
     278                selection.add(osm);
     279        } else {
     280            // nodes
     281            for (Node n : nc.getData().nodes) {
     282                if (!n.deleted && !n.incomplete && r.contains(nc.getPoint(n.eastNorth)))
     283                    selection.add(n);
     284            }
     285
     286            // ways
     287            for (Way w : nc.getData().ways) {
     288                if (w.deleted || w.nodes.isEmpty() || w.incomplete)
     289                        continue;
     290                if (alt) {
     291                    for (Node n : w.nodes) {
     292                        if (!n.incomplete && r.contains(nc.getPoint(n.eastNorth))) {
     293                            selection.add(w);
     294                            break;
     295                        }
     296                    }
     297                } else {
     298                    boolean allIn = true;
     299                    for (Node n : w.nodes) {
     300                        if (!n.incomplete && !r.contains(nc.getPoint(n.eastNorth))) {
     301                            allIn = false;
     302                            break;
     303                        }
     304                    }
     305                    if (allIn) selection.add(w);
     306                }
     307            }
     308        }
     309        return selection;
     310    }
     311
     312    public void mouseClicked(MouseEvent e) {}
     313    public void mouseEntered(MouseEvent e) {}
     314    public void mouseExited(MouseEvent e) {}
     315    public void mouseMoved(MouseEvent e) {}
    316316}
  • trunk/src/org/openstreetmap/josm/gui/SideButton.java

    r1103 r1169  
    1212
    1313public class SideButton extends JButton {
    14         public SideButton(Action action)
    15         {
    16                 super(action);
    17                 doStyle();
    18                 setText(null);
    19         }
    20         public SideButton(String imagename, String property, String tooltip, ActionListener actionListener)
    21         {
    22                 super(ImageProvider.get("dialogs", imagename));
    23                 doStyle();
    24                 setActionCommand(imagename);
    25                 addActionListener(actionListener);
    26                 setToolTipText(tooltip);
    27         }
    28         @Deprecated
    29         public SideButton(String name, String imagename, String property, String tooltip, int mnemonic, ActionListener actionListener)
    30         {
    31                 super(tr(name), ImageProvider.get("dialogs", imagename));
    32                 setMnemonic(mnemonic);
    33                 setup(name, property, tooltip, actionListener);
    34         }
    35         public SideButton(String name, String imagename, String property, String tooltip, Shortcut shortcut, ActionListener actionListener)
    36         {
    37                 super(tr(name), ImageProvider.get("dialogs", imagename));
    38                 if(shortcut != null)
     14    public SideButton(Action action)
     15    {
     16        super(action);
     17        doStyle();
     18        setText(null);
     19    }
     20    public SideButton(String imagename, String property, String tooltip, ActionListener actionListener)
     21    {
     22        super(ImageProvider.get("dialogs", imagename));
     23        doStyle();
     24        setActionCommand(imagename);
     25        addActionListener(actionListener);
     26        setToolTipText(tooltip);
     27    }
     28    @Deprecated
     29    public SideButton(String name, String imagename, String property, String tooltip, int mnemonic, ActionListener actionListener)
     30    {
     31        super(tr(name), ImageProvider.get("dialogs", imagename));
     32        setMnemonic(mnemonic);
     33        setup(name, property, tooltip, actionListener);
     34    }
     35    public SideButton(String name, String imagename, String property, String tooltip, Shortcut shortcut, ActionListener actionListener)
     36    {
     37        super(tr(name), ImageProvider.get("dialogs", imagename));
     38        if(shortcut != null)
    3939                {
    40                         shortcut.setMnemonic(this);
     40            shortcut.setMnemonic(this);
    4141                        if(tooltip != null)
    42                                 tooltip = Main.platform.makeTooltip(tooltip, shortcut);
     42                tooltip = Main.platform.makeTooltip(tooltip, shortcut);
    4343                }
    44                 setup(name, property, tooltip, actionListener);
    45         }
    46         public SideButton(String name, String imagename, String property, String tooltip, ActionListener actionListener)
    47         {
    48                 super(tr(name), ImageProvider.get("dialogs", imagename));
    49                 setup(name, property, tooltip, actionListener);
    50         }
    51         private void setup(String name, String property, String tooltip, ActionListener actionListener)
    52         {
    53                 doStyle();
    54                 setActionCommand(name);
    55                 addActionListener(actionListener);
    56                 setToolTipText(tooltip);
    57                 putClientProperty("help", "Dialog/"+property+"/"+name);
    58         }
    59         private void doStyle()
    60         {
    61                 setMargin(new Insets(1,1,1,1));
    62                 setIconTextGap(2);
    63         }
     44        setup(name, property, tooltip, actionListener);
     45    }
     46    public SideButton(String name, String imagename, String property, String tooltip, ActionListener actionListener)
     47    {
     48        super(tr(name), ImageProvider.get("dialogs", imagename));
     49        setup(name, property, tooltip, actionListener);
     50    }
     51    private void setup(String name, String property, String tooltip, ActionListener actionListener)
     52    {
     53        doStyle();
     54        setActionCommand(name);
     55        addActionListener(actionListener);
     56        setToolTipText(tooltip);
     57        putClientProperty("help", "Dialog/"+property+"/"+name);
     58    }
     59    private void doStyle()
     60    {
     61        setMargin(new Insets(1,1,1,1));
     62        setIconTextGap(2);
     63    }
    6464}
  • trunk/src/org/openstreetmap/josm/gui/SplashScreen.java

    r1138 r1169  
    3030/**
    3131 * Show a splash screen so the user knows what is happening during startup.
    32  * 
    33  * @author cbrill 
     32 *
     33 * @author cbrill
    3434 */
    3535public class SplashScreen extends JWindow {
    3636
    37         private JLabel status;
    38         private boolean visible;
     37    private JLabel status;
     38    private boolean visible;
    3939
    40         private Runnable closerRunner;
     40    private Runnable closerRunner;
    4141
    42         public SplashScreen(boolean visible) {
    43                 super();
    44                 this.visible=visible;
     42    public SplashScreen(boolean visible) {
     43        super();
     44        this.visible=visible;
    4545
    46                 if (!visible)
    47                         return;
     46        if (!visible)
     47            return;
    4848
    49                 // Add a nice border to the main splash screen
    50                 JPanel contentPane = (JPanel)this.getContentPane();
    51                 Border margin = new EtchedBorder(1, Color.white, Color.gray);
    52                 contentPane.setBorder(margin);
     49        // Add a nice border to the main splash screen
     50        JPanel contentPane = (JPanel)this.getContentPane();
     51        Border margin = new EtchedBorder(1, Color.white, Color.gray);
     52        contentPane.setBorder(margin);
    5353
    54                 // Add a margin from the border to the content
    55                 JPanel innerContentPane = new JPanel();
    56                 innerContentPane.setBorder(new EmptyBorder(10, 10, 2, 10));
    57                 contentPane.add(innerContentPane);
    58                 innerContentPane.setLayout(new GridBagLayout());
     54        // Add a margin from the border to the content
     55        JPanel innerContentPane = new JPanel();
     56        innerContentPane.setBorder(new EmptyBorder(10, 10, 2, 10));
     57        contentPane.add(innerContentPane);
     58        innerContentPane.setLayout(new GridBagLayout());
    5959
    60                 // Add the logo
    61                 JLabel logo = new JLabel(ImageProvider.get("logo.png"));
    62                 GridBagConstraints gbc = new GridBagConstraints();
    63                 gbc.gridheight = 2;
    64                 innerContentPane.add(logo, gbc);
     60        // Add the logo
     61        JLabel logo = new JLabel(ImageProvider.get("logo.png"));
     62        GridBagConstraints gbc = new GridBagConstraints();
     63        gbc.gridheight = 2;
     64        innerContentPane.add(logo, gbc);
    6565
    66                 // Add the name of this application
    67                 JLabel caption = new JLabel(tr("JOSM - Java OpenStreetMap Editor"));
    68                 caption.setFont(new Font("Helvetica", Font.BOLD, 20));
    69                 gbc.gridheight = 1;
    70                 gbc.gridx = 1;
    71                 gbc.insets = new Insets(30, 0, 0, 0);
    72                 innerContentPane.add(caption, gbc);
     66        // Add the name of this application
     67        JLabel caption = new JLabel(tr("JOSM - Java OpenStreetMap Editor"));
     68        caption.setFont(new Font("Helvetica", Font.BOLD, 20));
     69        gbc.gridheight = 1;
     70        gbc.gridx = 1;
     71        gbc.insets = new Insets(30, 0, 0, 0);
     72        innerContentPane.add(caption, gbc);
    7373
    74                 // Add the version number
    75                 JLabel version = new JLabel(tr("Version {0}", AboutAction.getVersionString()));
    76                 gbc.gridy = 1;
    77                 gbc.insets = new Insets(0, 0, 0, 0);
    78                 innerContentPane.add(version, gbc);
     74        // Add the version number
     75        JLabel version = new JLabel(tr("Version {0}", AboutAction.getVersionString()));
     76        gbc.gridy = 1;
     77        gbc.insets = new Insets(0, 0, 0, 0);
     78        innerContentPane.add(version, gbc);
    7979
    80                 // Add a separator to the status text
    81                 JSeparator separator = new JSeparator(JSeparator.HORIZONTAL);
    82                 gbc.gridx = 0;
    83                 gbc.gridy = 2;
    84                 gbc.gridwidth = 2;
    85                 gbc.fill = GridBagConstraints.HORIZONTAL;
    86                 gbc.insets = new Insets(15, 0, 5, 0);
    87                 innerContentPane.add(separator, gbc);
     80        // Add a separator to the status text
     81        JSeparator separator = new JSeparator(JSeparator.HORIZONTAL);
     82        gbc.gridx = 0;
     83        gbc.gridy = 2;
     84        gbc.gridwidth = 2;
     85        gbc.fill = GridBagConstraints.HORIZONTAL;
     86        gbc.insets = new Insets(15, 0, 5, 0);
     87        innerContentPane.add(separator, gbc);
    8888
    89                 // Add a status message
    90                 status = new JLabel();
    91                 gbc.gridy = 3;
    92                 gbc.insets = new Insets(0, 0, 0, 0);
    93                 innerContentPane.add(status, gbc);
    94                 setStatus(tr("Initializing"));
     89        // Add a status message
     90        status = new JLabel();
     91        gbc.gridy = 3;
     92        gbc.insets = new Insets(0, 0, 0, 0);
     93        innerContentPane.add(status, gbc);
     94        setStatus(tr("Initializing"));
    9595
    96                 pack();
     96        pack();
    9797
    98                 // Center the splash screen
    99                 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
    100                 Dimension labelSize = contentPane.getPreferredSize();
    101                 setLocation(screenSize.width / 2 - (labelSize.width / 2),
    102                         screenSize.height / 2 - (labelSize.height / 2));
     98        // Center the splash screen
     99        Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
     100        Dimension labelSize = contentPane.getPreferredSize();
     101        setLocation(screenSize.width / 2 - (labelSize.width / 2),
     102                screenSize.height / 2 - (labelSize.height / 2));
    103103
    104                 // Method to close the splash screen when being clicked or when closeSplash is called
    105                 closerRunner = new Runnable() {
    106                         public void run() {
    107                                 setVisible(false);
    108                                 dispose();
    109                         }
    110                 };
     104        // Method to close the splash screen when being clicked or when closeSplash is called
     105        closerRunner = new Runnable() {
     106            public void run() {
     107                setVisible(false);
     108                dispose();
     109            }
     110        };
    111111
    112                 // Add ability to hide splash screen by clicking it
    113                 addMouseListener(new MouseAdapter() {
    114                         public void mousePressed(MouseEvent event) {
    115                                 try {
    116                                         closerRunner.run();
    117                                 } catch (Exception e) {
    118                                         e.printStackTrace();
    119                                         // can catch InvocationTargetException
    120                                         // can catch InterruptedException
    121                                 }
    122                         }
    123                 });
    124                
     112        // Add ability to hide splash screen by clicking it
     113        addMouseListener(new MouseAdapter() {
     114            public void mousePressed(MouseEvent event) {
     115                try {
     116                    closerRunner.run();
     117                } catch (Exception e) {
     118                    e.printStackTrace();
     119                    // can catch InvocationTargetException
     120                    // can catch InterruptedException
     121                }
     122            }
     123        });
     124
    125125        // Hide splashscreen when other window is created
    126                 Toolkit.getDefaultToolkit().addAWTEventListener(awtListener, AWTEvent.WINDOW_EVENT_MASK);
    127                
    128                 setVisible(true);
    129         }
    130        
    131         private AWTEventListener awtListener = new AWTEventListener() {
    132                 public void eventDispatched(AWTEvent event) {
    133                         if (event.getSource() != SplashScreen.this) {
    134                                 closeSplash();
    135                         }
    136                 }               
    137         };
     126        Toolkit.getDefaultToolkit().addAWTEventListener(awtListener, AWTEvent.WINDOW_EVENT_MASK);
    138127
    139         /**
    140          * This method sets the status message. It should be called prior to
    141          * actually doing the action.
    142          *
    143          * @param message
    144          *            the message to be displayed
    145          */
    146         public void setStatus(String message) {
    147                 if (!visible)
    148                         return;
    149                 status.setText(message + " ...");
    150         }
     128        setVisible(true);
     129    }
    151130
    152         /**
    153          * Closes the splashscreen. Call once you are done starting.
    154          */
    155         public void closeSplash() {
    156                 if (!visible)
    157                         return;
    158                 Toolkit.getDefaultToolkit().removeAWTEventListener(awtListener);
    159                 try {
    160                         SwingUtilities.invokeLater(closerRunner);
    161                 } catch (Exception e) {
    162                         e.printStackTrace();
    163                         // can catch InvocationTargetException
    164                         // can catch InterruptedException
    165                 }
    166         }
     131    private AWTEventListener awtListener = new AWTEventListener() {
     132        public void eventDispatched(AWTEvent event) {
     133            if (event.getSource() != SplashScreen.this) {
     134                closeSplash();
     135            }
     136        }
     137    };
     138
     139    /**
     140     * This method sets the status message. It should be called prior to
     141     * actually doing the action.
     142     *
     143     * @param message
     144     *            the message to be displayed
     145     */
     146    public void setStatus(String message) {
     147        if (!visible)
     148            return;
     149        status.setText(message + " ...");
     150    }
     151
     152    /**
     153     * Closes the splashscreen. Call once you are done starting.
     154     */
     155    public void closeSplash() {
     156        if (!visible)
     157            return;
     158        Toolkit.getDefaultToolkit().removeAWTEventListener(awtListener);
     159        try {
     160            SwingUtilities.invokeLater(closerRunner);
     161        } catch (Exception e) {
     162            e.printStackTrace();
     163            // can catch InvocationTargetException
     164            // can catch InterruptedException
     165        }
     166    }
    167167
    168168}
  • trunk/src/org/openstreetmap/josm/gui/dialogs/CommandStackDialog.java

    r1084 r1169  
    2424public class CommandStackDialog extends ToggleDialog implements CommandQueueListener {
    2525
    26         private DefaultTreeModel treeModel = new DefaultTreeModel(new DefaultMutableTreeNode());
     26    private DefaultTreeModel treeModel = new DefaultTreeModel(new DefaultMutableTreeNode());
    2727    private JTree tree = new JTree(treeModel);
    2828
    29         public CommandStackDialog(final MapFrame mapFrame) {
    30                 super(tr("Command Stack"), "commandstack", tr("Open a list of all commands (undo buffer)."),
    31                 Shortcut.registerShortcut("subwindow:commandstack", tr("Toggle: {0}", tr("Command Stack")), KeyEvent.VK_O, Shortcut.GROUP_LAYER), 100);
    32                 Main.main.undoRedo.listenerCommands.add(this);
     29    public CommandStackDialog(final MapFrame mapFrame) {
     30        super(tr("Command Stack"), "commandstack", tr("Open a list of all commands (undo buffer)."),
     31        Shortcut.registerShortcut("subwindow:commandstack", tr("Toggle: {0}", tr("Command Stack")), KeyEvent.VK_O, Shortcut.GROUP_LAYER), 100);
     32        Main.main.undoRedo.listenerCommands.add(this);
    3333
    34                 tree.setRootVisible(false);
    35                 tree.setShowsRootHandles(true);
    36                 tree.expandRow(0);
    37                 tree.setCellRenderer(new DefaultTreeCellRenderer(){
    38                         @Override public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) {
    39                                 super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus);
    40                                 DefaultMutableTreeNode v = (DefaultMutableTreeNode)value;
    41                                 if (v.getUserObject() instanceof JLabel) {
    42                                         JLabel l = (JLabel)v.getUserObject();
    43                                         setIcon(l.getIcon());
    44                                         setText(l.getText());
    45                                 }
    46                                 return this;
    47                         }
    48                 });
    49                 tree.setVisibleRowCount(8);
    50                 add(new JScrollPane(tree), BorderLayout.CENTER);
    51         }
     34        tree.setRootVisible(false);
     35        tree.setShowsRootHandles(true);
     36        tree.expandRow(0);
     37        tree.setCellRenderer(new DefaultTreeCellRenderer(){
     38            @Override public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) {
     39                super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus);
     40                DefaultMutableTreeNode v = (DefaultMutableTreeNode)value;
     41                if (v.getUserObject() instanceof JLabel) {
     42                    JLabel l = (JLabel)v.getUserObject();
     43                    setIcon(l.getIcon());
     44                    setText(l.getText());
     45                }
     46                return this;
     47            }
     48        });
     49        tree.setVisibleRowCount(8);
     50        add(new JScrollPane(tree), BorderLayout.CENTER);
     51    }
    5252
    53         @Override public void setVisible(boolean v) {
    54                 if (v)
    55                         buildList();
    56                 else if (tree != null)
    57                         treeModel.setRoot(new DefaultMutableTreeNode());
    58                 super.setVisible(v);
    59         }
     53    @Override public void setVisible(boolean v) {
     54        if (v)
     55            buildList();
     56        else if (tree != null)
     57            treeModel.setRoot(new DefaultMutableTreeNode());
     58        super.setVisible(v);
     59    }
    6060
    61         private void buildList() {
    62                 if (Main.map == null || Main.map.mapView == null || Main.map.mapView.editLayer == null)
    63                         return;
    64                 Collection<Command> commands = Main.main.undoRedo.commands;
    65                 DefaultMutableTreeNode root = new DefaultMutableTreeNode();
    66                 for (Command c : commands)
    67                         root.add(c.description());
    68                 treeModel.setRoot(root);
    69                 tree.scrollRowToVisible(treeModel.getChildCount(root)-1);
    70         }
     61    private void buildList() {
     62        if (Main.map == null || Main.map.mapView == null || Main.map.mapView.editLayer == null)
     63            return;
     64        Collection<Command> commands = Main.main.undoRedo.commands;
     65        DefaultMutableTreeNode root = new DefaultMutableTreeNode();
     66        for (Command c : commands)
     67            root.add(c.description());
     68        treeModel.setRoot(root);
     69        tree.scrollRowToVisible(treeModel.getChildCount(root)-1);
     70    }
    7171
    72         public void commandChanged(int queueSize, int redoSize) {
    73                 if (!isVisible())
    74                         return;
     72    public void commandChanged(int queueSize, int redoSize) {
     73        if (!isVisible())
     74            return;
    7575        treeModel.setRoot(new DefaultMutableTreeNode());
    76                 buildList();
     76        buildList();
    7777    }
    7878}
  • trunk/src/org/openstreetmap/josm/gui/dialogs/ConflictDialog.java

    r1117 r1169  
    4747public final class ConflictDialog extends ToggleDialog {
    4848
    49         public final Map<OsmPrimitive, OsmPrimitive> conflicts = new HashMap<OsmPrimitive, OsmPrimitive>();
    50         private final DefaultListModel model = new DefaultListModel();
    51         private final JList displaylist = new JList(model);
     49    public final Map<OsmPrimitive, OsmPrimitive> conflicts = new HashMap<OsmPrimitive, OsmPrimitive>();
     50    private final DefaultListModel model = new DefaultListModel();
     51    private final JList displaylist = new JList(model);
    5252
    53         public ConflictDialog() {
    54                 super(tr("Conflict"), "conflict", tr("Merging conflicts."),
    55                 Shortcut.registerShortcut("subwindow:conflict", tr("Toggle: {0}", tr("Conflict")), KeyEvent.VK_C, Shortcut.GROUP_LAYER), 100);
    56                 displaylist.setCellRenderer(new OsmPrimitivRenderer());
    57                 displaylist.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
    58                 displaylist.addMouseListener(new MouseAdapter(){
    59                         @Override public void mouseClicked(MouseEvent e) {
    60                                 if (e.getClickCount() >= 2)
    61                                         resolve();
    62                         }
    63                 });
    64                 add(new JScrollPane(displaylist), BorderLayout.CENTER);
     53    public ConflictDialog() {
     54        super(tr("Conflict"), "conflict", tr("Merging conflicts."),
     55        Shortcut.registerShortcut("subwindow:conflict", tr("Toggle: {0}", tr("Conflict")), KeyEvent.VK_C, Shortcut.GROUP_LAYER), 100);
     56        displaylist.setCellRenderer(new OsmPrimitivRenderer());
     57        displaylist.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
     58        displaylist.addMouseListener(new MouseAdapter(){
     59            @Override public void mouseClicked(MouseEvent e) {
     60                if (e.getClickCount() >= 2)
     61                    resolve();
     62            }
     63        });
     64        add(new JScrollPane(displaylist), BorderLayout.CENTER);
    6565
    66                 JPanel buttonPanel = new JPanel(new GridLayout(1,2));
    67                 buttonPanel.add(new SideButton(marktr("Resolve"), "conflict", "Conflict",
    68                 tr("Open a merge dialog of all selected items in the list above."), new ActionListener(){
    69                         public void actionPerformed(ActionEvent e) {
    70                                 resolve();
    71                         }
    72                 }));
     66        JPanel buttonPanel = new JPanel(new GridLayout(1,2));
     67        buttonPanel.add(new SideButton(marktr("Resolve"), "conflict", "Conflict",
     68        tr("Open a merge dialog of all selected items in the list above."), new ActionListener(){
     69            public void actionPerformed(ActionEvent e) {
     70                resolve();
     71            }
     72        }));
    7373
    74                 buttonPanel.add(new SideButton(marktr("Select"), "select", "Conflict",
    75                 tr("Set the selected elements on the map to the selected items in the list above."), new ActionListener(){
    76                         public void actionPerformed(ActionEvent e) {
    77                                 Collection<OsmPrimitive> sel = new LinkedList<OsmPrimitive>();
    78                                 for (Object o : displaylist.getSelectedValues())
    79                                         sel.add((OsmPrimitive)o);
    80                                 Main.ds.setSelected(sel);
    81                         }
    82                 }));
    83                 add(buttonPanel, BorderLayout.SOUTH);
     74        buttonPanel.add(new SideButton(marktr("Select"), "select", "Conflict",
     75        tr("Set the selected elements on the map to the selected items in the list above."), new ActionListener(){
     76            public void actionPerformed(ActionEvent e) {
     77                Collection<OsmPrimitive> sel = new LinkedList<OsmPrimitive>();
     78                for (Object o : displaylist.getSelectedValues())
     79                    sel.add((OsmPrimitive)o);
     80                Main.ds.setSelected(sel);
     81            }
     82        }));
     83        add(buttonPanel, BorderLayout.SOUTH);
    8484
    85                 DataSet.selListeners.add(new SelectionChangedListener(){
    86                         public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) {
    87                                 displaylist.clearSelection();
    88                                 for (OsmPrimitive osm : newSelection) {
    89                                         if (conflicts.containsKey(osm)) {
    90                                                 int pos = model.indexOf(osm);
    91                                                 displaylist.addSelectionInterval(pos, pos);
    92                                         }
    93                                 }
    94                         }
    95                 });
    96                 displaylist.getSelectionModel().addListSelectionListener(new ListSelectionListener(){
    97                         public void valueChanged(ListSelectionEvent e) {
    98                                 Main.map.mapView.repaint();
    99                         }
    100                 });
    101         }
     85        DataSet.selListeners.add(new SelectionChangedListener(){
     86            public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) {
     87                displaylist.clearSelection();
     88                for (OsmPrimitive osm : newSelection) {
     89                    if (conflicts.containsKey(osm)) {
     90                        int pos = model.indexOf(osm);
     91                        displaylist.addSelectionInterval(pos, pos);
     92                    }
     93                }
     94            }
     95        });
     96        displaylist.getSelectionModel().addListSelectionListener(new ListSelectionListener(){
     97            public void valueChanged(ListSelectionEvent e) {
     98                Main.map.mapView.repaint();
     99            }
     100        });
     101    }
    102102
    103         private final void resolve() {
    104                 if (displaylist.getSelectedIndex() == -1) {
    105                         JOptionPane.showMessageDialog(Main.parent,tr("Please select something from the conflict list."));
    106                         return;
    107                 }
    108                 Map<OsmPrimitive, OsmPrimitive> sel = new HashMap<OsmPrimitive, OsmPrimitive>();
    109                 for (int i : displaylist.getSelectedIndices()) {
    110                         OsmPrimitive s = (OsmPrimitive)model.get(i);
    111                         sel.put(s, conflicts.get(s));
    112                 }
    113                 ConflictResolver resolver = new ConflictResolver(sel);
    114                 int answer = JOptionPane.showConfirmDialog(Main.parent, resolver, tr("Resolve Conflicts"), JOptionPane.OK_CANCEL_OPTION);
    115                 if (answer != JOptionPane.OK_OPTION)
    116                         return;
    117                 Main.main.undoRedo.add(new ConflictResolveCommand(resolver.conflicts, sel));
    118                 Main.map.mapView.repaint();
    119         }
     103    private final void resolve() {
     104        if (displaylist.getSelectedIndex() == -1) {
     105            JOptionPane.showMessageDialog(Main.parent,tr("Please select something from the conflict list."));
     106            return;
     107        }
     108        Map<OsmPrimitive, OsmPrimitive> sel = new HashMap<OsmPrimitive, OsmPrimitive>();
     109        for (int i : displaylist.getSelectedIndices()) {
     110            OsmPrimitive s = (OsmPrimitive)model.get(i);
     111            sel.put(s, conflicts.get(s));
     112        }
     113        ConflictResolver resolver = new ConflictResolver(sel);
     114        int answer = JOptionPane.showConfirmDialog(Main.parent, resolver, tr("Resolve Conflicts"), JOptionPane.OK_CANCEL_OPTION);
     115        if (answer != JOptionPane.OK_OPTION)
     116            return;
     117        Main.main.undoRedo.add(new ConflictResolveCommand(resolver.conflicts, sel));
     118        Main.map.mapView.repaint();
     119    }
    120120
    121         public final void rebuildList() {
    122                 model.removeAllElements();
    123                 for (OsmPrimitive osm : this.conflicts.keySet())
    124                         if (osm instanceof Node)
    125                                 model.addElement(osm);
    126                 for (OsmPrimitive osm : this.conflicts.keySet())
    127                         if (osm instanceof Way)
    128                                 model.addElement(osm);
     121    public final void rebuildList() {
     122        model.removeAllElements();
     123        for (OsmPrimitive osm : this.conflicts.keySet())
     124            if (osm instanceof Node)
     125                model.addElement(osm);
     126        for (OsmPrimitive osm : this.conflicts.keySet())
     127            if (osm instanceof Way)
     128                model.addElement(osm);
    129129        for (OsmPrimitive osm : this.conflicts.keySet())
    130130            if (osm instanceof Relation)
     
    132132    }
    133133
    134         public final void add(Map<OsmPrimitive, OsmPrimitive> conflicts) {
    135                 this.conflicts.putAll(conflicts);
    136                 rebuildList();
    137         }
     134    public final void add(Map<OsmPrimitive, OsmPrimitive> conflicts) {
     135        this.conflicts.putAll(conflicts);
     136        rebuildList();
     137    }
    138138
    139         /**
    140         * Paint all conflicts that can be expressed on the main window.
    141         */
    142         public void paintConflicts(final Graphics g, final NavigatableComponent nc) {
    143                 Color preferencesColor = Main.pref.getColor("conflict", Color.gray);
    144                 if (preferencesColor.equals(Color.BLACK))
    145                         return;
    146                 g.setColor(preferencesColor);
    147                 Visitor conflictPainter = new Visitor(){
    148                         public void visit(Node n) {
    149                                 Point p = nc.getPoint(n.eastNorth);
    150                                 g.drawRect(p.x-1, p.y-1, 2, 2);
    151                         }
    152                         public void visit(Node n1, Node n2) {
    153                                 Point p1 = nc.getPoint(n1.eastNorth);
    154                                 Point p2 = nc.getPoint(n2.eastNorth);
    155                                 g.drawLine(p1.x, p1.y, p2.x, p2.y);
    156                         }
    157                         public void visit(Way w) {
    158                                 Node lastN = null;
    159                                 for (Node n : w.nodes) {
    160                                         if (lastN == null) {
    161                                                 lastN = n;
    162                                                 continue;
    163                                         }
    164                                         visit(lastN, n);
    165                                         lastN = n;
    166                                 }
    167                         }
    168                         public void visit(Relation e) {
    169                                 for (RelationMember em : e.members)
    170                                         em.member.visit(this);
    171                         }
    172                 };
    173                 for (Object o : displaylist.getSelectedValues())
    174                         conflicts.get(o).visit(conflictPainter);
    175         }
     139    /**
     140    * Paint all conflicts that can be expressed on the main window.
     141    */
     142    public void paintConflicts(final Graphics g, final NavigatableComponent nc) {
     143        Color preferencesColor = Main.pref.getColor("conflict", Color.gray);
     144        if (preferencesColor.equals(Color.BLACK))
     145            return;
     146        g.setColor(preferencesColor);
     147        Visitor conflictPainter = new Visitor(){
     148            public void visit(Node n) {
     149                Point p = nc.getPoint(n.eastNorth);
     150                g.drawRect(p.x-1, p.y-1, 2, 2);
     151            }
     152            public void visit(Node n1, Node n2) {
     153                Point p1 = nc.getPoint(n1.eastNorth);
     154                Point p2 = nc.getPoint(n2.eastNorth);
     155                g.drawLine(p1.x, p1.y, p2.x, p2.y);
     156            }
     157            public void visit(Way w) {
     158                Node lastN = null;
     159                for (Node n : w.nodes) {
     160                    if (lastN == null) {
     161                        lastN = n;
     162                        continue;
     163                    }
     164                    visit(lastN, n);
     165                    lastN = n;
     166                }
     167            }
     168            public void visit(Relation e) {
     169                for (RelationMember em : e.members)
     170                    em.member.visit(this);
     171            }
     172        };
     173        for (Object o : displaylist.getSelectedValues())
     174            conflicts.get(o).visit(conflictPainter);
     175    }
    176176}
  • trunk/src/org/openstreetmap/josm/gui/dialogs/HistoryDialog.java

    r1084 r1169  
    5454public class HistoryDialog extends ToggleDialog implements SelectionChangedListener {
    5555
    56         public static final Date unifyDate(Date d) {
    57                 Calendar c = Calendar.getInstance();
    58                 c.setTime(d);
    59                 c.set(Calendar.MINUTE, 0);
    60                 c.set(Calendar.SECOND, 0);
    61                 return c.getTime();
    62         }
     56    public static final Date unifyDate(Date d) {
     57        Calendar c = Calendar.getInstance();
     58        c.setTime(d);
     59        c.set(Calendar.MINUTE, 0);
     60        c.set(Calendar.SECOND, 0);
     61        return c.getTime();
     62    }
    6363
    64         private static class HistoryItem implements Comparable<HistoryItem> {
    65                 OsmPrimitive osm;
    66                 boolean visible;
     64    private static class HistoryItem implements Comparable<HistoryItem> {
     65        OsmPrimitive osm;
     66        boolean visible;
    6767
    68                 public int compareTo(HistoryItem o) {
    69                         return unifyDate(osm.getTimestamp()).compareTo(unifyDate(o.osm.getTimestamp()));
    70                 }
    71         }
     68        public int compareTo(HistoryItem o) {
     69            return unifyDate(osm.getTimestamp()).compareTo(unifyDate(o.osm.getTimestamp()));
     70        }
     71    }
    7272
    73         private final DefaultTableModel data = new DefaultTableModel(){
    74                 @Override public boolean isCellEditable(int row, int column) {
    75                         return false;
    76                 }
    77         };
     73    private final DefaultTableModel data = new DefaultTableModel(){
     74        @Override public boolean isCellEditable(int row, int column) {
     75            return false;
     76        }
     77    };
    7878
    79         /**
    80         * Main table. 3 columns:
    81         * Object | Date | visible (icon, no text)
    82         */
    83         private JTable history = new JTable(data);
    84         private JScrollPane historyPane = new JScrollPane(history);
     79    /**
     80    * Main table. 3 columns:
     81    * Object | Date | visible (icon, no text)
     82    */
     83    private JTable history = new JTable(data);
     84    private JScrollPane historyPane = new JScrollPane(history);
    8585
    86         private Map<OsmPrimitive, List<HistoryItem>> cache = new HashMap<OsmPrimitive, List<HistoryItem>>();
    87         private JLabel notLoaded = new JLabel("<html><i>"+tr("Click Reload to refresh list")+"</i></html>");
     86    private Map<OsmPrimitive, List<HistoryItem>> cache = new HashMap<OsmPrimitive, List<HistoryItem>>();
     87    private JLabel notLoaded = new JLabel("<html><i>"+tr("Click Reload to refresh list")+"</i></html>");
    8888
    89         public HistoryDialog() {
    90                 super(tr("History"), "history", tr("Display the history of all selected items."),
    91                 Shortcut.registerShortcut("subwindow:history", tr("Toggle: {0}", tr("History")), KeyEvent.VK_H,
    92                 Shortcut.GROUP_LAYER, Shortcut.SHIFT_DEFAULT), 150);
    93                 historyPane.setVisible(false);
    94                 notLoaded.setVisible(true);
    95                 notLoaded.setHorizontalAlignment(JLabel.CENTER);
     89    public HistoryDialog() {
     90        super(tr("History"), "history", tr("Display the history of all selected items."),
     91        Shortcut.registerShortcut("subwindow:history", tr("Toggle: {0}", tr("History")), KeyEvent.VK_H,
     92        Shortcut.GROUP_LAYER, Shortcut.SHIFT_DEFAULT), 150);
     93        historyPane.setVisible(false);
     94        notLoaded.setVisible(true);
     95        notLoaded.setHorizontalAlignment(JLabel.CENTER);
    9696
    97                 history.setDefaultRenderer(Object.class, new DefaultTableCellRenderer(){
    98                         @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
    99                                 return super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
    100                         }
    101                 });
    102                 data.setColumnIdentifiers(new Object[]{tr("Object"),tr("Date"),""});
    103                 history.getColumnModel().getColumn(0).setPreferredWidth(200);
    104                 history.getColumnModel().getColumn(1).setPreferredWidth(200);
    105                 history.getColumnModel().getColumn(2).setPreferredWidth(20);
    106                 final TableCellRenderer oldRenderer = history.getTableHeader().getDefaultRenderer();
    107                 history.getTableHeader().setDefaultRenderer(new DefaultTableCellRenderer(){
    108                         @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
    109                                 JComponent c = (JComponent)oldRenderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
    110                                 if (!value.equals(""))
    111                                         return c;
    112                                 JLabel l = new JLabel(ImageProvider.get("misc","showhide"));
    113                                 l.setForeground(c.getForeground());
    114                                 l.setBackground(c.getBackground());
    115                                 l.setFont(c.getFont());
    116                                 l.setBorder(c.getBorder());
    117                                 l.setOpaque(true);
    118                                 return l;
    119                         }
    120                 });
     97        history.setDefaultRenderer(Object.class, new DefaultTableCellRenderer(){
     98            @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
     99                return super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
     100            }
     101        });
     102        data.setColumnIdentifiers(new Object[]{tr("Object"),tr("Date"),""});
     103        history.getColumnModel().getColumn(0).setPreferredWidth(200);
     104        history.getColumnModel().getColumn(1).setPreferredWidth(200);
     105        history.getColumnModel().getColumn(2).setPreferredWidth(20);
     106        final TableCellRenderer oldRenderer = history.getTableHeader().getDefaultRenderer();
     107        history.getTableHeader().setDefaultRenderer(new DefaultTableCellRenderer(){
     108            @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
     109                JComponent c = (JComponent)oldRenderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
     110                if (!value.equals(""))
     111                    return c;
     112                JLabel l = new JLabel(ImageProvider.get("misc","showhide"));
     113                l.setForeground(c.getForeground());
     114                l.setBackground(c.getBackground());
     115                l.setFont(c.getFont());
     116                l.setBorder(c.getBorder());
     117                l.setOpaque(true);
     118                return l;
     119            }
     120        });
    121121
    122                 JPanel centerPanel = new JPanel(new GridBagLayout());
    123                 centerPanel.add(notLoaded, GBC.eol().fill(GBC.BOTH));
    124                 centerPanel.add(historyPane, GBC.eol().fill(GBC.BOTH));
    125                 add(centerPanel, BorderLayout.CENTER);
     122        JPanel centerPanel = new JPanel(new GridBagLayout());
     123        centerPanel.add(notLoaded, GBC.eol().fill(GBC.BOTH));
     124        centerPanel.add(historyPane, GBC.eol().fill(GBC.BOTH));
     125        add(centerPanel, BorderLayout.CENTER);
    126126
    127                 JPanel buttons = new JPanel(new GridLayout(1,2));
    128                 buttons.add(new SideButton(marktr("Reload"), "refresh", "History", tr("Reload all currently selected objects and refresh the list."),
    129                 new ActionListener(){
    130                         public void actionPerformed(ActionEvent e) {
    131                                 reload();
    132                         }
    133                 }));
    134                 buttons.add(new SideButton(marktr("Revert"), "revert", "History",
    135                 tr("Revert the state of all currently selected objects to the version selected in the history list."), new ActionListener(){
    136                         public void actionPerformed(ActionEvent e) {
    137                                 JOptionPane.showMessageDialog(Main.parent, tr("Not implemented yet."));
    138                         }
    139                 }));
    140                 add(buttons, BorderLayout.SOUTH);
     127        JPanel buttons = new JPanel(new GridLayout(1,2));
     128        buttons.add(new SideButton(marktr("Reload"), "refresh", "History", tr("Reload all currently selected objects and refresh the list."),
     129        new ActionListener(){
     130            public void actionPerformed(ActionEvent e) {
     131                reload();
     132            }
     133        }));
     134        buttons.add(new SideButton(marktr("Revert"), "revert", "History",
     135        tr("Revert the state of all currently selected objects to the version selected in the history list."), new ActionListener(){
     136            public void actionPerformed(ActionEvent e) {
     137                JOptionPane.showMessageDialog(Main.parent, tr("Not implemented yet."));
     138            }
     139        }));
     140        add(buttons, BorderLayout.SOUTH);
    141141
    142                 DataSet.selListeners.add(this);
    143         }
     142        DataSet.selListeners.add(this);
     143    }
    144144
    145145
    146         @Override public void setVisible(boolean b) {
    147                 super.setVisible(b);
    148                 if (b)
    149                         update();
    150         }
     146    @Override public void setVisible(boolean b) {
     147        super.setVisible(b);
     148        if (b)
     149            update();
     150    }
    151151
    152152
    153         public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) {
    154                 if (isVisible())
    155                         update();
    156         }
     153    public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) {
     154        if (isVisible())
     155            update();
     156    }
    157157
    158         /**
    159         * Identify all new objects in the selection and if any, hide the list.
    160         * Else, update the list with the selected items shown.
    161         */
    162         private void update() {
    163                 Collection<OsmPrimitive> sel = Main.ds.getSelected();
    164                 if (!cache.keySet().containsAll(sel)) {
    165                         historyPane.setVisible(false);
    166                         notLoaded.setVisible(true);
    167                 } else {
    168                         SortedSet<HistoryItem> orderedHistory = new TreeSet<HistoryItem>();
    169                         for (OsmPrimitive osm : sel)
    170                                 orderedHistory.addAll(cache.get(osm));
    171                         data.setRowCount(0);
    172                         for (HistoryItem i : orderedHistory)
    173                                 data.addRow(new Object[]{i.osm, i.osm.timestamp, i.visible});
    174                         historyPane.setVisible(true);
    175                         notLoaded.setVisible(false);
    176                 }
    177         }
     158    /**
     159    * Identify all new objects in the selection and if any, hide the list.
     160    * Else, update the list with the selected items shown.
     161    */
     162    private void update() {
     163        Collection<OsmPrimitive> sel = Main.ds.getSelected();
     164        if (!cache.keySet().containsAll(sel)) {
     165            historyPane.setVisible(false);
     166            notLoaded.setVisible(true);
     167        } else {
     168            SortedSet<HistoryItem> orderedHistory = new TreeSet<HistoryItem>();
     169            for (OsmPrimitive osm : sel)
     170                orderedHistory.addAll(cache.get(osm));
     171            data.setRowCount(0);
     172            for (HistoryItem i : orderedHistory)
     173                data.addRow(new Object[]{i.osm, i.osm.timestamp, i.visible});
     174            historyPane.setVisible(true);
     175            notLoaded.setVisible(false);
     176        }
     177    }
    178178
    179         void reload() {
    180                 JOptionPane.showMessageDialog(Main.parent, tr("Not implemented yet."));
    181         }
     179    void reload() {
     180        JOptionPane.showMessageDialog(Main.parent, tr("Not implemented yet."));
     181    }
    182182}
  • trunk/src/org/openstreetmap/josm/gui/dialogs/LayerListDialog.java

    r1084 r1169  
    5050public class LayerListDialog extends ToggleDialog implements LayerChangeListener {
    5151
    52         /**
    53         * The last layerlist created. Used to update the list in the Show/Hide and Delete actions.
    54         * TODO: Replace with Listener-Pattern.
    55         */
    56         static JList instance;
    57         private JScrollPane listScrollPane;
    58 
    59         public final static class DeleteLayerAction extends AbstractAction {
    60 
    61                 private final Layer layer;
    62 
    63                 public DeleteLayerAction(Layer layer) {
    64                         super(tr("Delete"), ImageProvider.get("dialogs", "delete"));
    65                         putValue(SHORT_DESCRIPTION, tr("Delete the selected layer."));
    66                         putValue("help", "Action/LayerDelete");
    67                         this.layer = layer;
    68                 }
    69 
    70                 public void actionPerformed(ActionEvent e) {
    71                         int sel = instance.getSelectedIndex();
    72                         Layer l = layer != null ? layer : (Layer)instance.getSelectedValue();
    73                         if(l == null)
    74                                 return;
    75                         if (l instanceof OsmDataLayer)
    76                         {
    77                                 if (((OsmDataLayer)l).isModified())
    78                                 {
    79                                         if(JOptionPane.showConfirmDialog(instance, tr("There are unsaved changes. Delete the layer anwyay?"),
    80                                         tr("Unsaved Changes"), JOptionPane.YES_NO_OPTION) != JOptionPane.YES_OPTION)
    81                                                 return;
    82                                 }
    83                                 else if(!DontShowAgainInfo.show("delete_layer", tr("Do you really want to delete the whole layer?"), false))
    84                                         return;
    85                         }
    86                         Main.main.removeLayer(l);
    87                         if (sel >= instance.getModel().getSize())
    88                                 sel = instance.getModel().getSize()-1;
    89                         if (instance.getSelectedValue() == null)
    90                                 instance.setSelectedIndex(sel);
    91                         if (Main.map != null)
    92                                 Main.map.mapView.setActiveLayer((Layer)instance.getSelectedValue());
    93                 }
    94         }
    95 
    96         public final static class ShowHideLayerAction extends AbstractAction {
    97                 private final Layer layer;
    98 
    99                 public ShowHideLayerAction(Layer layer) {
    100                         super(tr("Show/Hide"), ImageProvider.get("dialogs", "showhide"));
    101                         putValue(SHORT_DESCRIPTION, tr("Toggle visible state of the selected layer."));
    102                         putValue("help", "Action/LayerShowHide");
    103                         this.layer = layer;
    104                 }
    105 
    106                 public void actionPerformed(ActionEvent e) {
    107                         Layer l = layer == null ? (Layer)instance.getSelectedValue() : layer;
    108                         if(l == null)
    109                                 return;
    110                         l.visible = !l.visible;
    111                         Main.map.mapView.repaint();
    112                         instance.repaint();
    113                 }
    114         }
    115 
    116         public final static class ShowHideMarkerText extends AbstractAction {
    117                 private final Layer layer;
    118 
    119                 public ShowHideMarkerText(Layer layer) {
    120                         super(tr("Show/Hide Text/Icons"), ImageProvider.get("dialogs", "showhide"));
    121                         putValue(SHORT_DESCRIPTION, tr("Toggle visible state of the marker text and icons."));
    122                         putValue("help", "Action/ShowHideTextIcons");
    123                         this.layer = layer;
    124                 }
    125 
    126                 public void actionPerformed(ActionEvent e) {
    127                         Layer l = layer == null ? (Layer)instance.getSelectedValue() : layer;
    128                         String current = Main.pref.get("marker.show "+l.name,"show");
    129                         Main.pref.put("marker.show "+l.name, current.equalsIgnoreCase("show") ? "hide" : "show");
    130                         Main.map.mapView.repaint();
    131                         instance.repaint();
    132                 }
    133         }
    134 
    135         /**
    136         * The data model for the list component.
    137         */
    138         DefaultListModel model = new DefaultListModel();
    139         /**
    140         * The merge action. This is only called, if the current selection and its
    141         * item below are editable datasets and the merge button is clicked.
    142         */
    143         private final SideButton mergeButton;
    144         /**
    145         * Button for moving layer up.
    146         */
    147         private final SideButton upButton;
    148         /**
    149         * Button for moving layer down.
    150         */
    151         private final SideButton downButton;
    152         /**
    153         * Button for delete layer.
    154         */
    155         private Action deleteAction = new DeleteLayerAction(null);
    156 
    157         /**
    158         * Create an layerlist and attach it to the given mapView.
    159         */
    160         public LayerListDialog(MapFrame mapFrame) {
    161                 super(tr("Layers"), "layerlist", tr("Open a list of all loaded layers."),
    162                 Shortcut.registerShortcut("subwindow:layers", tr("Toggle: {0}", tr("Layers")), KeyEvent.VK_L, Shortcut.GROUP_LAYER), 100);
    163                 instance = new JList(model);
    164                 listScrollPane = new JScrollPane(instance);
    165                 add(listScrollPane, BorderLayout.CENTER);
    166                 instance.setBackground(UIManager.getColor("Button.background"));
    167                 instance.setCellRenderer(new DefaultListCellRenderer(){
    168                         @Override public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
    169                                 Layer layer = (Layer)value;
    170                                 JLabel label = (JLabel)super.getListCellRendererComponent(list,
    171                                                 layer.name, index, isSelected, cellHasFocus);
    172                                 Icon icon = layer.getIcon();
    173                                 if (!layer.visible)
    174                                         icon = ImageProvider.overlay(icon, "overlay/invisible", OverlayPosition.SOUTHEAST);
    175                                 label.setIcon(icon);
    176                                 label.setToolTipText(layer.getToolTipText());
    177                                 return label;
    178                         }
    179                 });
    180 
    181                 final MapView mapView = mapFrame.mapView;
    182 
    183                 Collection<Layer> data = mapView.getAllLayers();
    184                 for (Layer l : data)
    185                         model.addElement(l);
    186 
    187                 instance.setSelectedValue(mapView.getActiveLayer(), true);
    188                 instance.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
    189                 instance.addListSelectionListener(new ListSelectionListener(){
    190                         public void valueChanged(ListSelectionEvent e) {
    191                                 if (instance.getModel().getSize() == 0)
    192                                         return;
    193                                 if (instance.getSelectedIndex() == -1)
    194                                         instance.setSelectedIndex(e.getFirstIndex());
    195                                 mapView.setActiveLayer((Layer)instance.getSelectedValue());
    196                         }
    197                 });
    198                 Layer.listeners.add(this);
    199 
    200                 instance.addMouseListener(new MouseAdapter(){
    201                         private void openPopup(MouseEvent e) {
    202                                 Point p = listScrollPane.getMousePosition();
    203                                 if (p == null)
    204                                         return; // user is faster than swing with mouse movement
    205                                 int index = instance.locationToIndex(e.getPoint());
    206                                 Layer layer = (Layer)instance.getModel().getElementAt(index);
    207                                 LayerListPopup menu = new LayerListPopup(instance, layer);
    208                                 menu.show(listScrollPane, p.x, p.y-3);
    209                         }
    210                         @Override public void mousePressed(MouseEvent e) {
    211                                 if (e.isPopupTrigger())
    212                                         openPopup(e);
    213                         }
    214                         @Override public void mouseReleased(MouseEvent e) {
    215                                 if (e.isPopupTrigger())
    216                                         openPopup(e);
    217                         }
    218                         @Override public void mouseClicked(MouseEvent e) {
    219                                 if (e.getClickCount() == 2) {
    220                                         int index = instance.locationToIndex(e.getPoint());
    221                                         Layer layer = (Layer)instance.getModel().getElementAt(index);
    222                                         String current = Main.pref.get("marker.show "+layer.name,"show");
    223                                         Main.pref.put("marker.show "+layer.name, current.equalsIgnoreCase("show") ? "hide" : "show");
    224                                         layer.visible = !layer.visible;
    225                                         Main.map.mapView.repaint();
    226                                         instance.repaint();
    227                                 }
    228                         }
    229                 });
    230 
    231 
    232                 // Buttons
    233                 JPanel buttonPanel = new JPanel(new GridLayout(1, 5));
    234 
    235                 ActionListener upDown = new ActionListener(){
    236                         public void actionPerformed(ActionEvent e) {
    237                                 Layer l = (Layer)instance.getSelectedValue();
    238                                 int sel = instance.getSelectedIndex();
    239                                 int selDest = e.getActionCommand().equals("up") ? sel-1 : sel+1;
    240                                 mapView.moveLayer(l, selDest);
    241                                 model.set(sel, model.get(selDest));
    242                                 model.set(selDest, l);
    243                                 instance.setSelectedIndex(selDest);
    244                                 updateButtonEnabled();
    245                                 mapView.repaint();
    246                         }
    247                 };
    248 
    249                 upButton = new SideButton("up", "LayerList", tr("Move the selected layer one row up."), upDown);
    250                 buttonPanel.add(upButton);
    251 
    252                 downButton = new SideButton("down", "LayerList", tr("Move the selected layer one row down."), upDown);
    253                 buttonPanel.add(downButton);
    254 
    255                 buttonPanel.add(new SideButton(new ShowHideLayerAction(null)));
    256                 buttonPanel.add(new SideButton(deleteAction));
    257 
    258                 mergeButton = new SideButton("Merge", "mergedown", "LayerList", tr("Merge the layer directly below into the selected layer."),
    259                 new ActionListener(){
    260                         public void actionPerformed(ActionEvent e) {
    261                                 Layer lTo = (Layer)instance.getSelectedValue();
    262                                 Layer lFrom = (Layer)model.get(instance.getSelectedIndex()+1);
    263                                 lTo.mergeFrom(lFrom);
    264                                 mapView.removeLayer(lFrom);
    265                                 updateButtonEnabled();
    266                                 mapView.repaint();
    267                         }
    268                 });
    269                 mergeButton.setText(null);
    270                 buttonPanel.add(mergeButton);
    271 
    272                 add(buttonPanel, BorderLayout.SOUTH);
    273 
    274                 updateButtonEnabled();
    275         }
    276 
    277         /**
    278         * Updates the state of the Buttons.
    279         */
    280         void updateButtonEnabled() {
    281                 int sel = instance.getSelectedIndex();
    282                 Layer l = (Layer)instance.getSelectedValue();
    283                 boolean enable = model.getSize() > 1;
    284                 enable = enable && sel < model.getSize()-1;
    285                 enable = enable && ((Layer)model.get(sel+1)).isMergable(l);
    286                 mergeButton.setEnabled(enable);
    287                 upButton.setEnabled(sel > 0);
    288                 downButton.setEnabled(sel >= 0 && sel < model.getSize()-1);
    289                 deleteAction.setEnabled(!model.isEmpty());
    290         }
    291 
    292         /**
    293         * Add the new layer to the list.
    294         */
    295         public void layerAdded(Layer newLayer) {
    296                 model.add(model.size(), newLayer);
    297                 updateButtonEnabled();
    298         }
    299 
    300         public void layerRemoved(Layer oldLayer) {
    301                 model.removeElement(oldLayer);
    302                 if (model.isEmpty()) {
    303                         Layer.listeners.remove(this);
    304                         return;
    305                 }
    306                 if (instance.getSelectedIndex() == -1)
    307                         instance.setSelectedIndex(0);
    308                 updateButtonEnabled();
    309         }
    310 
    311         /**
    312         * If the newLayer is not the actual selection, select it.
    313         */
    314         public void activeLayerChange(Layer oldLayer, Layer newLayer) {
    315                 if (newLayer != instance.getSelectedValue())
    316                         instance.setSelectedValue(newLayer, true);
    317                 updateButtonEnabled();
    318         }
     52    /**
     53    * The last layerlist created. Used to update the list in the Show/Hide and Delete actions.
     54    * TODO: Replace with Listener-Pattern.
     55    */
     56    static JList instance;
     57    private JScrollPane listScrollPane;
     58
     59    public final static class DeleteLayerAction extends AbstractAction {
     60
     61        private final Layer layer;
     62
     63        public DeleteLayerAction(Layer layer) {
     64            super(tr("Delete"), ImageProvider.get("dialogs", "delete"));
     65            putValue(SHORT_DESCRIPTION, tr("Delete the selected layer."));
     66            putValue("help", "Action/LayerDelete");
     67            this.layer = layer;
     68        }
     69
     70        public void actionPerformed(ActionEvent e) {
     71            int sel = instance.getSelectedIndex();
     72            Layer l = layer != null ? layer : (Layer)instance.getSelectedValue();
     73            if(l == null)
     74                return;
     75            if (l instanceof OsmDataLayer)
     76            {
     77                if (((OsmDataLayer)l).isModified())
     78                {
     79                    if(JOptionPane.showConfirmDialog(instance, tr("There are unsaved changes. Delete the layer anwyay?"),
     80                    tr("Unsaved Changes"), JOptionPane.YES_NO_OPTION) != JOptionPane.YES_OPTION)
     81                        return;
     82                }
     83                else if(!DontShowAgainInfo.show("delete_layer", tr("Do you really want to delete the whole layer?"), false))
     84                    return;
     85            }
     86            Main.main.removeLayer(l);
     87            if (sel >= instance.getModel().getSize())
     88                sel = instance.getModel().getSize()-1;
     89            if (instance.getSelectedValue() == null)
     90                instance.setSelectedIndex(sel);
     91            if (Main.map != null)
     92                Main.map.mapView.setActiveLayer((Layer)instance.getSelectedValue());
     93        }
     94    }
     95
     96    public final static class ShowHideLayerAction extends AbstractAction {
     97        private final Layer layer;
     98
     99        public ShowHideLayerAction(Layer layer) {
     100            super(tr("Show/Hide"), ImageProvider.get("dialogs", "showhide"));
     101            putValue(SHORT_DESCRIPTION, tr("Toggle visible state of the selected layer."));
     102            putValue("help", "Action/LayerShowHide");
     103            this.layer = layer;
     104        }
     105
     106        public void actionPerformed(ActionEvent e) {
     107            Layer l = layer == null ? (Layer)instance.getSelectedValue() : layer;
     108            if(l == null)
     109                return;
     110            l.visible = !l.visible;
     111            Main.map.mapView.repaint();
     112            instance.repaint();
     113        }
     114    }
     115
     116    public final static class ShowHideMarkerText extends AbstractAction {
     117        private final Layer layer;
     118
     119        public ShowHideMarkerText(Layer layer) {
     120            super(tr("Show/Hide Text/Icons"), ImageProvider.get("dialogs", "showhide"));
     121            putValue(SHORT_DESCRIPTION, tr("Toggle visible state of the marker text and icons."));
     122            putValue("help", "Action/ShowHideTextIcons");
     123            this.layer = layer;
     124        }
     125
     126        public void actionPerformed(ActionEvent e) {
     127            Layer l = layer == null ? (Layer)instance.getSelectedValue() : layer;
     128            String current = Main.pref.get("marker.show "+l.name,"show");
     129            Main.pref.put("marker.show "+l.name, current.equalsIgnoreCase("show") ? "hide" : "show");
     130            Main.map.mapView.repaint();
     131            instance.repaint();
     132        }
     133    }
     134
     135    /**
     136    * The data model for the list component.
     137    */
     138    DefaultListModel model = new DefaultListModel();
     139    /**
     140    * The merge action. This is only called, if the current selection and its
     141    * item below are editable datasets and the merge button is clicked.
     142    */
     143    private final SideButton mergeButton;
     144    /**
     145    * Button for moving layer up.
     146    */
     147    private final SideButton upButton;
     148    /**
     149    * Button for moving layer down.
     150    */
     151    private final SideButton downButton;
     152    /**
     153    * Button for delete layer.
     154    */
     155    private Action deleteAction = new DeleteLayerAction(null);
     156
     157    /**
     158    * Create an layerlist and attach it to the given mapView.
     159    */
     160    public LayerListDialog(MapFrame mapFrame) {
     161        super(tr("Layers"), "layerlist", tr("Open a list of all loaded layers."),
     162        Shortcut.registerShortcut("subwindow:layers", tr("Toggle: {0}", tr("Layers")), KeyEvent.VK_L, Shortcut.GROUP_LAYER), 100);
     163        instance = new JList(model);
     164        listScrollPane = new JScrollPane(instance);
     165        add(listScrollPane, BorderLayout.CENTER);
     166        instance.setBackground(UIManager.getColor("Button.background"));
     167        instance.setCellRenderer(new DefaultListCellRenderer(){
     168            @Override public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
     169                Layer layer = (Layer)value;
     170                JLabel label = (JLabel)super.getListCellRendererComponent(list,
     171                        layer.name, index, isSelected, cellHasFocus);
     172                Icon icon = layer.getIcon();
     173                if (!layer.visible)
     174                    icon = ImageProvider.overlay(icon, "overlay/invisible", OverlayPosition.SOUTHEAST);
     175                label.setIcon(icon);
     176                label.setToolTipText(layer.getToolTipText());
     177                return label;
     178            }
     179        });
     180
     181        final MapView mapView = mapFrame.mapView;
     182
     183        Collection<Layer> data = mapView.getAllLayers();
     184        for (Layer l : data)
     185            model.addElement(l);
     186
     187        instance.setSelectedValue(mapView.getActiveLayer(), true);
     188        instance.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
     189        instance.addListSelectionListener(new ListSelectionListener(){
     190            public void valueChanged(ListSelectionEvent e) {
     191                if (instance.getModel().getSize() == 0)
     192                    return;
     193                if (instance.getSelectedIndex() == -1)
     194                    instance.setSelectedIndex(e.getFirstIndex());
     195                mapView.setActiveLayer((Layer)instance.getSelectedValue());
     196            }
     197        });
     198        Layer.listeners.add(this);
     199
     200        instance.addMouseListener(new MouseAdapter(){
     201            private void openPopup(MouseEvent e) {
     202                Point p = listScrollPane.getMousePosition();
     203                if (p == null)
     204                    return; // user is faster than swing with mouse movement
     205                int index = instance.locationToIndex(e.getPoint());
     206                Layer layer = (Layer)instance.getModel().getElementAt(index);
     207                LayerListPopup menu = new LayerListPopup(instance, layer);
     208                menu.show(listScrollPane, p.x, p.y-3);
     209            }
     210            @Override public void mousePressed(MouseEvent e) {
     211                if (e.isPopupTrigger())
     212                    openPopup(e);
     213            }
     214            @Override public void mouseReleased(MouseEvent e) {
     215                if (e.isPopupTrigger())
     216                    openPopup(e);
     217            }
     218            @Override public void mouseClicked(MouseEvent e) {
     219                if (e.getClickCount() == 2) {
     220                    int index = instance.locationToIndex(e.getPoint());
     221                    Layer layer = (Layer)instance.getModel().getElementAt(index);
     222                    String current = Main.pref.get("marker.show "+layer.name,"show");
     223                    Main.pref.put("marker.show "+layer.name, current.equalsIgnoreCase("show") ? "hide" : "show");
     224                    layer.visible = !layer.visible;
     225                    Main.map.mapView.repaint();
     226                    instance.repaint();
     227                }
     228            }
     229        });
     230
     231
     232        // Buttons
     233        JPanel buttonPanel = new JPanel(new GridLayout(1, 5));
     234
     235        ActionListener upDown = new ActionListener(){
     236            public void actionPerformed(ActionEvent e) {
     237                Layer l = (Layer)instance.getSelectedValue();
     238                int sel = instance.getSelectedIndex();
     239                int selDest = e.getActionCommand().equals("up") ? sel-1 : sel+1;
     240                mapView.moveLayer(l, selDest);
     241                model.set(sel, model.get(selDest));
     242                model.set(selDest, l);
     243                instance.setSelectedIndex(selDest);
     244                updateButtonEnabled();
     245                mapView.repaint();
     246            }
     247        };
     248
     249        upButton = new SideButton("up", "LayerList", tr("Move the selected layer one row up."), upDown);
     250        buttonPanel.add(upButton);
     251
     252        downButton = new SideButton("down", "LayerList", tr("Move the selected layer one row down."), upDown);
     253        buttonPanel.add(downButton);
     254
     255        buttonPanel.add(new SideButton(new ShowHideLayerAction(null)));
     256        buttonPanel.add(new SideButton(deleteAction));
     257
     258        mergeButton = new SideButton("Merge", "mergedown", "LayerList", tr("Merge the layer directly below into the selected layer."),
     259        new ActionListener(){
     260            public void actionPerformed(ActionEvent e) {
     261                Layer lTo = (Layer)instance.getSelectedValue();
     262                Layer lFrom = (Layer)model.get(instance.getSelectedIndex()+1);
     263                lTo.mergeFrom(lFrom);
     264                mapView.removeLayer(lFrom);
     265                updateButtonEnabled();
     266                mapView.repaint();
     267            }
     268        });
     269        mergeButton.setText(null);
     270        buttonPanel.add(mergeButton);
     271
     272        add(buttonPanel, BorderLayout.SOUTH);
     273
     274        updateButtonEnabled();
     275    }
     276
     277    /**
     278    * Updates the state of the Buttons.
     279    */
     280    void updateButtonEnabled() {
     281        int sel = instance.getSelectedIndex();
     282        Layer l = (Layer)instance.getSelectedValue();
     283        boolean enable = model.getSize() > 1;
     284        enable = enable && sel < model.getSize()-1;
     285        enable = enable && ((Layer)model.get(sel+1)).isMergable(l);
     286        mergeButton.setEnabled(enable);
     287        upButton.setEnabled(sel > 0);
     288        downButton.setEnabled(sel >= 0 && sel < model.getSize()-1);
     289        deleteAction.setEnabled(!model.isEmpty());
     290    }
     291
     292    /**
     293    * Add the new layer to the list.
     294    */
     295    public void layerAdded(Layer newLayer) {
     296        model.add(model.size(), newLayer);
     297        updateButtonEnabled();
     298    }
     299
     300    public void layerRemoved(Layer oldLayer) {
     301        model.removeElement(oldLayer);
     302        if (model.isEmpty()) {
     303            Layer.listeners.remove(this);
     304            return;
     305        }
     306        if (instance.getSelectedIndex() == -1)
     307            instance.setSelectedIndex(0);
     308        updateButtonEnabled();
     309    }
     310
     311    /**
     312    * If the newLayer is not the actual selection, select it.
     313    */
     314    public void activeLayerChange(Layer oldLayer, Layer newLayer) {
     315        if (newLayer != instance.getSelectedValue())
     316            instance.setSelectedValue(newLayer, true);
     317        updateButtonEnabled();
     318    }
    319319}
  • trunk/src/org/openstreetmap/josm/gui/dialogs/LayerListPopup.java

    r627 r1169  
    2121public class LayerListPopup extends JPopupMenu {
    2222
    23         public final static class InfoAction extends AbstractAction {
    24             private final Layer layer;
    25             public InfoAction(Layer layer) {
    26                 super(tr("Info"), ImageProvider.get("info"));
    27                     this.layer = layer;
    28             }
    29             public void actionPerformed(ActionEvent e) {
    30                 JOptionPane.showMessageDialog(Main.parent, layer.getInfoComponent());
    31             }
     23    public final static class InfoAction extends AbstractAction {
     24        private final Layer layer;
     25        public InfoAction(Layer layer) {
     26            super(tr("Info"), ImageProvider.get("info"));
     27            this.layer = layer;
     28        }
     29        public void actionPerformed(ActionEvent e) {
     30            JOptionPane.showMessageDialog(Main.parent, layer.getInfoComponent());
     31        }
    3232    }
    3333
    34         public LayerListPopup(final JList layers, final Layer layer) {
    35                 for (Component c : layer.getMenuEntries())
    36                         add(c);
    37         }
     34    public LayerListPopup(final JList layers, final Layer layer) {
     35        for (Component c : layer.getMenuEntries())
     36            add(c);
     37    }
    3838}
  • trunk/src/org/openstreetmap/josm/gui/dialogs/PropertiesDialog.java

    r1168 r1169  
    8585public class PropertiesDialog extends ToggleDialog implements SelectionChangedListener {
    8686
    87         /**
    88         * Used to display relation names in the membership table
    89         */
    90         private NameVisitor nameVisitor = new NameVisitor();
    91 
    92         /**
    93         * Watches for double clicks and from editing or new property, depending on the
    94         * location, the click was.
    95         * @author imi
    96         */
    97         public class DblClickWatch extends MouseAdapter {
    98                 @Override public void mouseClicked(MouseEvent e) {
    99                         if (e.getClickCount() < 2)
    100                         {
    101                                 if (e.getSource() == propertyTable)
    102                                         membershipTable.clearSelection();
    103                                 else if (e.getSource() == membershipTable)
    104                                         propertyTable.clearSelection();
    105                         }
    106                         else if (e.getSource() == propertyTable)
    107                         {
    108                                 int row = propertyTable.rowAtPoint(e.getPoint());
    109                                 if (row > -1)
    110                                         propertyEdit(row);
    111                         } else if (e.getSource() == membershipTable) {
    112                                 int row = membershipTable.rowAtPoint(e.getPoint());
    113                                 if (row > -1)
    114                                         membershipEdit(row);
    115                         }
    116                         else
    117                         {
    118                                 add();
    119                         }
    120                 }
    121         }
    122 
    123         private final Map<String, Map<String, Integer>> valueCount = new TreeMap<String, Map<String, Integer>>();
    124         /**
    125         * Edit the value in the properties table row
    126         * @param row The row of the table from which the value is edited.
    127         */
    128         void propertyEdit(int row) {
    129                 String key = propertyData.getValueAt(row, 0).toString();
    130                 objKey=key;
    131                 Collection<OsmPrimitive> sel = Main.ds.getSelected();
    132                 if (sel.isEmpty()) {
    133                         JOptionPane.showMessageDialog(Main.parent, tr("Please select the objects you want to change properties for."));
    134                         return;
    135                 }
    136                 String msg = "<html>"+trn("This will change up to {0} object.", "This will change up to {0} objects.", sel.size(), sel.size())+"<br><br>("+tr("An empty value deletes the key.", key)+")</html>";
    137 
    138                 JPanel panel = new JPanel(new BorderLayout());
    139                 panel.add(new JLabel(msg), BorderLayout.NORTH);
    140 
    141                 final TreeMap<String, TreeSet<String>> allData = createAutoCompletionInfo(true);
    142 
    143                 JPanel p = new JPanel(new GridBagLayout());
    144                 panel.add(p, BorderLayout.CENTER);
    145 
    146                 final AutoCompleteComboBox keys = new AutoCompleteComboBox();
    147                 keys.setPossibleItems(allData.keySet());
    148                 keys.setEditable(true);
    149                 keys.setSelectedItem(key);
    150 
    151                 p.add(new JLabel(tr("Key")), GBC.std());
    152                 p.add(Box.createHorizontalStrut(10), GBC.std());
    153                 p.add(keys, GBC.eol().fill(GBC.HORIZONTAL));
    154 
    155                 final AutoCompleteComboBox values = new AutoCompleteComboBox();
    156                 values.setRenderer(new DefaultListCellRenderer() {
    157                         @Override public Component getListCellRendererComponent(JList list,  Object value, int index, boolean isSelected,  boolean cellHasFocus) {
    158                             Component c = super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
    159                             if (c instanceof JLabel) {
    160                                 String str = null;
    161                                         str=(String) value;
    162                                         if (valueCount.containsKey(objKey)){
    163                                                 Map<String, Integer> m=valueCount.get(objKey);
    164                                                 if (m.containsKey(str)) {
    165                                                         str+="("+m.get(str)+")";
    166                                                         c.setFont(c.getFont().deriveFont(Font.ITALIC+Font.BOLD));
    167                                                 }
    168                                         }
    169                                     ((JLabel)c).setText(str);
    170                             }
    171                             return c;
    172                         }
    173                 });
    174                 values.setEditable(true);
    175                 updateListData(key, allData, values);
    176                 Map<String, Integer> m=(Map<String, Integer>)propertyData.getValueAt(row, 1);
    177                 final String selection= m.size()!=1?tr("<different>"):m.entrySet().iterator().next().getKey();
    178                 values.setSelectedItem(selection);
    179                 values.getEditor().setItem(selection);
    180                 p.add(new JLabel(tr("Value")), GBC.std());
    181                 p.add(Box.createHorizontalStrut(10), GBC.std());
    182                 p.add(values, GBC.eol().fill(GBC.HORIZONTAL));
    183                 addFocusAdapter(row, allData, keys, values);
    184 
    185                 final JOptionPane optionPane = new JOptionPane(panel, JOptionPane.QUESTION_MESSAGE, JOptionPane.OK_CANCEL_OPTION) {
    186                         @Override public void selectInitialValue() {
    187                                 values.requestFocusInWindow();
    188                                 values.getEditor().selectAll();
    189                         }
    190                 };
    191                 final JDialog dlg = optionPane.createDialog(Main.parent, tr("Change values?"));
    192 
    193                 values.getEditor().addActionListener(new ActionListener() {
    194                         public void actionPerformed(ActionEvent e) {
    195                                 dlg.setVisible(false);
    196                                 optionPane.setValue(JOptionPane.OK_OPTION);
    197                         }
    198                 });
    199 
    200                 String oldValue = values.getEditor().getItem().toString();
    201                 dlg.setVisible(true);
    202 
    203                 Object answer = optionPane.getValue();
    204                 if (answer == null || answer == JOptionPane.UNINITIALIZED_VALUE ||
    205                                 (answer instanceof Integer && (Integer)answer != JOptionPane.OK_OPTION)) {
    206                         values.getEditor().setItem(oldValue);
    207                         return;
    208                 }
    209 
    210                 String value = values.getEditor().getItem().toString();
    211                 // is not Java 1.5
    212                 //value = java.text.Normalizer.normalize(value, java.text.Normalizer.Form.NFC);
    213                 if (value.equals(""))
    214                         value = null; // delete the key
    215                 String newkey = keys.getEditor().getItem().toString();
    216                 //newkey = java.text.Normalizer.normalize(newkey, java.text.Normalizer.Form.NFC);
    217                 if (newkey.equals("")) {
    218                         newkey = key;
    219                         value = null; // delete the key instead
    220                 }
     87    /**
     88    * Used to display relation names in the membership table
     89    */
     90    private NameVisitor nameVisitor = new NameVisitor();
     91
     92    /**
     93    * Watches for double clicks and from editing or new property, depending on the
     94    * location, the click was.
     95    * @author imi
     96    */
     97    public class DblClickWatch extends MouseAdapter {
     98        @Override public void mouseClicked(MouseEvent e) {
     99            if (e.getClickCount() < 2)
     100            {
     101                if (e.getSource() == propertyTable)
     102                    membershipTable.clearSelection();
     103                else if (e.getSource() == membershipTable)
     104                    propertyTable.clearSelection();
     105            }
     106            else if (e.getSource() == propertyTable)
     107            {
     108                int row = propertyTable.rowAtPoint(e.getPoint());
     109                if (row > -1)
     110                    propertyEdit(row);
     111            } else if (e.getSource() == membershipTable) {
     112                int row = membershipTable.rowAtPoint(e.getPoint());
     113                if (row > -1)
     114                    membershipEdit(row);
     115            }
     116            else
     117            {
     118                add();
     119            }
     120        }
     121    }
     122
     123    private final Map<String, Map<String, Integer>> valueCount = new TreeMap<String, Map<String, Integer>>();
     124    /**
     125    * Edit the value in the properties table row
     126    * @param row The row of the table from which the value is edited.
     127    */
     128    void propertyEdit(int row) {
     129        String key = propertyData.getValueAt(row, 0).toString();
     130        objKey=key;
     131        Collection<OsmPrimitive> sel = Main.ds.getSelected();
     132        if (sel.isEmpty()) {
     133            JOptionPane.showMessageDialog(Main.parent, tr("Please select the objects you want to change properties for."));
     134            return;
     135        }
     136        String msg = "<html>"+trn("This will change up to {0} object.", "This will change up to {0} objects.", sel.size(), sel.size())+"<br><br>("+tr("An empty value deletes the key.", key)+")</html>";
     137
     138        JPanel panel = new JPanel(new BorderLayout());
     139        panel.add(new JLabel(msg), BorderLayout.NORTH);
     140
     141        final TreeMap<String, TreeSet<String>> allData = createAutoCompletionInfo(true);
     142
     143        JPanel p = new JPanel(new GridBagLayout());
     144        panel.add(p, BorderLayout.CENTER);
     145
     146        final AutoCompleteComboBox keys = new AutoCompleteComboBox();
     147        keys.setPossibleItems(allData.keySet());
     148        keys.setEditable(true);
     149        keys.setSelectedItem(key);
     150
     151        p.add(new JLabel(tr("Key")), GBC.std());
     152        p.add(Box.createHorizontalStrut(10), GBC.std());
     153        p.add(keys, GBC.eol().fill(GBC.HORIZONTAL));
     154
     155        final AutoCompleteComboBox values = new AutoCompleteComboBox();
     156        values.setRenderer(new DefaultListCellRenderer() {
     157            @Override public Component getListCellRendererComponent(JList list,  Object value, int index, boolean isSelected,  boolean cellHasFocus) {
     158                Component c = super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
     159                if (c instanceof JLabel) {
     160                    String str = null;
     161                        str=(String) value;
     162                        if (valueCount.containsKey(objKey)){
     163                            Map<String, Integer> m=valueCount.get(objKey);
     164                            if (m.containsKey(str)) {
     165                                str+="("+m.get(str)+")";
     166                                c.setFont(c.getFont().deriveFont(Font.ITALIC+Font.BOLD));
     167                            }
     168                        }
     169                    ((JLabel)c).setText(str);
     170                }
     171                return c;
     172            }
     173        });
     174        values.setEditable(true);
     175        updateListData(key, allData, values);
     176        Map<String, Integer> m=(Map<String, Integer>)propertyData.getValueAt(row, 1);
     177        final String selection= m.size()!=1?tr("<different>"):m.entrySet().iterator().next().getKey();
     178        values.setSelectedItem(selection);
     179        values.getEditor().setItem(selection);
     180        p.add(new JLabel(tr("Value")), GBC.std());
     181        p.add(Box.createHorizontalStrut(10), GBC.std());
     182        p.add(values, GBC.eol().fill(GBC.HORIZONTAL));
     183        addFocusAdapter(row, allData, keys, values);
     184
     185        final JOptionPane optionPane = new JOptionPane(panel, JOptionPane.QUESTION_MESSAGE, JOptionPane.OK_CANCEL_OPTION) {
     186            @Override public void selectInitialValue() {
     187                values.requestFocusInWindow();
     188                values.getEditor().selectAll();
     189            }
     190        };
     191        final JDialog dlg = optionPane.createDialog(Main.parent, tr("Change values?"));
     192
     193        values.getEditor().addActionListener(new ActionListener() {
     194            public void actionPerformed(ActionEvent e) {
     195                dlg.setVisible(false);
     196                optionPane.setValue(JOptionPane.OK_OPTION);
     197            }
     198        });
     199
     200        String oldValue = values.getEditor().getItem().toString();
     201        dlg.setVisible(true);
     202
     203        Object answer = optionPane.getValue();
     204        if (answer == null || answer == JOptionPane.UNINITIALIZED_VALUE ||
     205                (answer instanceof Integer && (Integer)answer != JOptionPane.OK_OPTION)) {
     206            values.getEditor().setItem(oldValue);
     207            return;
     208        }
     209
     210        String value = values.getEditor().getItem().toString();
     211        // is not Java 1.5
     212        //value = java.text.Normalizer.normalize(value, java.text.Normalizer.Form.NFC);
     213        if (value.equals(""))
     214            value = null; // delete the key
     215        String newkey = keys.getEditor().getItem().toString();
     216        //newkey = java.text.Normalizer.normalize(newkey, java.text.Normalizer.Form.NFC);
     217        if (newkey.equals("")) {
     218            newkey = key;
     219            value = null; // delete the key instead
     220        }
    221221        if (newkey.equals("created_by"))
    222222        {
     
    224224            return;
    225225        }
    226                 if (key.equals(newkey) || value == null)
    227                         Main.main.undoRedo.add(new ChangePropertyCommand(sel, newkey, value));
    228                 else {
    229                         Collection<Command> commands=new Vector<Command>();
    230                         commands.add(new ChangePropertyCommand(sel, key, null));
    231                         if (value.equals(tr("<different>"))) {
    232                                 HashMap<String, Vector<OsmPrimitive>> map=new HashMap<String, Vector<OsmPrimitive>>();
    233                                 for (OsmPrimitive osm: sel) {
    234                                         if(osm.keys != null)
    235                                         {
    236                                                 String val=osm.keys.get(key);
    237                                                 if(val != null)
    238                                                 {
    239                                                         if (map.containsKey(val)) {
    240                                                                 map.get(val).add(osm);
    241                                                         } else {
    242                                                                 Vector<OsmPrimitive> v = new Vector<OsmPrimitive>();
    243                                                                 v.add(osm);
    244                                                                 map.put(val, v);
    245                                                         }
    246                                                 }
    247                                         }
    248                                 }
    249                                 for (Entry<String, Vector<OsmPrimitive>> e: map.entrySet()) {
    250                                         commands.add(new ChangePropertyCommand(e.getValue(), newkey, e.getKey()));
    251                                 }
    252                         } else {
    253                                 commands.add(new ChangePropertyCommand(sel, newkey, value));
    254                         }
    255                         Main.main.undoRedo.add(new SequenceCommand(trn("Change properties of up to {0} object", "Change properties of up to {0} objects", sel.size(), sel.size()), commands));
    256                 }
    257 
    258                 Main.ds.fireSelectionChanged(sel);
    259                 selectionChanged(sel); // update whole table
    260                 Main.parent.repaint(); // repaint all - drawing could have been changed
    261         }
    262 
    263         /**
     226        if (key.equals(newkey) || value == null)
     227            Main.main.undoRedo.add(new ChangePropertyCommand(sel, newkey, value));
     228        else {
     229            Collection<Command> commands=new Vector<Command>();
     230            commands.add(new ChangePropertyCommand(sel, key, null));
     231            if (value.equals(tr("<different>"))) {
     232                HashMap<String, Vector<OsmPrimitive>> map=new HashMap<String, Vector<OsmPrimitive>>();
     233                for (OsmPrimitive osm: sel) {
     234                    if(osm.keys != null)
     235                    {
     236                        String val=osm.keys.get(key);
     237                        if(val != null)
     238                        {
     239                            if (map.containsKey(val)) {
     240                                map.get(val).add(osm);
     241                            } else {
     242                                Vector<OsmPrimitive> v = new Vector<OsmPrimitive>();
     243                                v.add(osm);
     244                                map.put(val, v);
     245                            }
     246                        }
     247                    }
     248                }
     249                for (Entry<String, Vector<OsmPrimitive>> e: map.entrySet()) {
     250                    commands.add(new ChangePropertyCommand(e.getValue(), newkey, e.getKey()));
     251                }
     252            } else {
     253                commands.add(new ChangePropertyCommand(sel, newkey, value));
     254            }
     255            Main.main.undoRedo.add(new SequenceCommand(trn("Change properties of up to {0} object", "Change properties of up to {0} objects", sel.size(), sel.size()), commands));
     256        }
     257
     258        Main.ds.fireSelectionChanged(sel);
     259        selectionChanged(sel); // update whole table
     260        Main.parent.repaint(); // repaint all - drawing could have been changed
     261    }
     262
     263    /**
    264264     * @param key
    265265     * @param allData
     
    267267     */
    268268    private void updateListData(String key, final TreeMap<String, TreeSet<String>> allData, final AutoCompleteComboBox values) {
    269             Collection<String> newItems;
    270             if (allData.containsKey(key)) {
    271                         newItems = allData.get(key);
    272                 } else {
    273                         newItems = Collections.emptyList();
    274                 }
    275                 values.setPossibleItems(newItems);
    276     }
    277 
    278         /**
    279         * This simply fires up an relation editor for the relation shown; everything else
    280         * is the editor's business.
    281         *
    282         * @param row
    283         */
    284         void membershipEdit(int row) {
    285                 final RelationEditor editor = new RelationEditor((Relation)membershipData.getValueAt(row, 0),
    286                                 (Collection<RelationMember>) membershipData.getValueAt(row, 1) );
    287                 editor.setVisible(true);
    288         }
    289 
    290         /**
    291         * Open the add selection dialog and add a new key/value to the table (and
    292         * to the dataset, of course).
    293         */
    294         void add() {
    295                 Collection<OsmPrimitive> sel = Main.ds.getSelected();
    296                 if (sel.isEmpty()) {
    297                         JOptionPane.showMessageDialog(Main.parent, tr("Please select objects for which you want to change properties."));
    298                         return;
    299                 }
    300 
    301                 JPanel p = new JPanel(new BorderLayout());
    302                 p.add(new JLabel("<html>"+trn("This will change up to {0} object.","This will change up to {0} objects.", sel.size(),sel.size())+"<br><br>"+tr("Please select a key")),
    303                                 BorderLayout.NORTH);
    304                 final TreeMap<String, TreeSet<String>> allData = createAutoCompletionInfo(false);
    305                 final AutoCompleteComboBox keys = new AutoCompleteComboBox();
    306                 keys.setPossibleItems(allData.keySet());
    307                 keys.setEditable(true);
    308 
    309                 p.add(keys, BorderLayout.CENTER);
    310 
    311                 JPanel p2 = new JPanel(new BorderLayout());
    312                 p.add(p2, BorderLayout.SOUTH);
    313                 p2.add(new JLabel(tr("Please select a value")), BorderLayout.NORTH);
    314                 final AutoCompleteComboBox values = new AutoCompleteComboBox();
    315                 values.setEditable(true);
    316                 p2.add(values, BorderLayout.CENTER);
    317 
    318                 addFocusAdapter(-1, allData, keys, values);
    319                 JOptionPane pane = new JOptionPane(p, JOptionPane.PLAIN_MESSAGE, JOptionPane.OK_CANCEL_OPTION){
    320                         @Override public void selectInitialValue() {
    321                                 keys.requestFocusInWindow();
    322                                 keys.getEditor().selectAll();
    323                         }
    324                 };
    325                 pane.createDialog(Main.parent, tr("Change values?")).setVisible(true);
    326                 if (!Integer.valueOf(JOptionPane.OK_OPTION).equals(pane.getValue()))
    327                         return;
    328                 String key = keys.getEditor().getItem().toString();
    329                 String value = values.getEditor().getItem().toString();
    330                 if (value.equals(""))
    331                         return;
     269        Collection<String> newItems;
     270        if (allData.containsKey(key)) {
     271            newItems = allData.get(key);
     272        } else {
     273            newItems = Collections.emptyList();
     274        }
     275        values.setPossibleItems(newItems);
     276    }
     277
     278    /**
     279    * This simply fires up an relation editor for the relation shown; everything else
     280    * is the editor's business.
     281    *
     282    * @param row
     283    */
     284    void membershipEdit(int row) {
     285        final RelationEditor editor = new RelationEditor((Relation)membershipData.getValueAt(row, 0),
     286                (Collection<RelationMember>) membershipData.getValueAt(row, 1) );
     287        editor.setVisible(true);
     288    }
     289
     290    /**
     291    * Open the add selection dialog and add a new key/value to the table (and
     292    * to the dataset, of course).
     293    */
     294    void add() {
     295        Collection<OsmPrimitive> sel = Main.ds.getSelected();
     296        if (sel.isEmpty()) {
     297            JOptionPane.showMessageDialog(Main.parent, tr("Please select objects for which you want to change properties."));
     298            return;
     299        }
     300
     301        JPanel p = new JPanel(new BorderLayout());
     302        p.add(new JLabel("<html>"+trn("This will change up to {0} object.","This will change up to {0} objects.", sel.size(),sel.size())+"<br><br>"+tr("Please select a key")),
     303                BorderLayout.NORTH);
     304        final TreeMap<String, TreeSet<String>> allData = createAutoCompletionInfo(false);
     305        final AutoCompleteComboBox keys = new AutoCompleteComboBox();
     306        keys.setPossibleItems(allData.keySet());
     307        keys.setEditable(true);
     308
     309        p.add(keys, BorderLayout.CENTER);
     310
     311        JPanel p2 = new JPanel(new BorderLayout());
     312        p.add(p2, BorderLayout.SOUTH);
     313        p2.add(new JLabel(tr("Please select a value")), BorderLayout.NORTH);
     314        final AutoCompleteComboBox values = new AutoCompleteComboBox();
     315        values.setEditable(true);
     316        p2.add(values, BorderLayout.CENTER);
     317
     318        addFocusAdapter(-1, allData, keys, values);
     319        JOptionPane pane = new JOptionPane(p, JOptionPane.PLAIN_MESSAGE, JOptionPane.OK_CANCEL_OPTION){
     320            @Override public void selectInitialValue() {
     321                keys.requestFocusInWindow();
     322                keys.getEditor().selectAll();
     323            }
     324        };
     325        pane.createDialog(Main.parent, tr("Change values?")).setVisible(true);
     326        if (!Integer.valueOf(JOptionPane.OK_OPTION).equals(pane.getValue()))
     327            return;
     328        String key = keys.getEditor().getItem().toString();
     329        String value = values.getEditor().getItem().toString();
     330        if (value.equals(""))
     331            return;
    332332        if (key.equals("created_by"))
    333333            return;
    334                 Main.main.undoRedo.add(new ChangePropertyCommand(sel, key, value));
    335                 Main.ds.fireSelectionChanged(sel);
    336                 selectionChanged(sel); // update table
    337                 Main.parent.repaint(); // repaint all - drawing could have been changed
    338         }
    339 
    340         /**
    341         * @param allData
    342         * @param keys
    343         * @param values
    344         */
    345         private void addFocusAdapter(final int row, final TreeMap<String, TreeSet<String>> allData,final AutoCompleteComboBox keys, final AutoCompleteComboBox values) {
    346                 // get the combo box' editor component
    347                 JTextComponent editor = (JTextComponent)values.getEditor()
    348                         .getEditorComponent();
    349                 // Refresh the values model when focus is gained
    350                 editor.addFocusListener(new FocusAdapter() {
    351                         @Override public void focusGained(FocusEvent e) {
    352                                 String key = keys.getEditor().getItem().toString();
    353                                 updateListData(key, allData, values);
    354                                 objKey=key;
    355                         }
    356                 });
    357         }
    358         private String objKey;
    359         /**
    360         * @return
    361         */
    362         private TreeMap<String, TreeSet<String>> createAutoCompletionInfo(
    363                 boolean edit) {
    364                 final TreeMap<String, TreeSet<String>> allData = new TreeMap<String, TreeSet<String>>();
    365                 for (OsmPrimitive osm : Main.ds.allNonDeletedPrimitives()) {
    366                         for (String key : osm.keySet()) {
    367                                 TreeSet<String> values = null;
    368                                 if (allData.containsKey(key))
    369                                         values = allData.get(key);
    370                                 else {
    371                                         values = new TreeSet<String>();
    372                                         allData.put(key, values);
    373                                 }
    374                                 values.add(osm.get(key));
    375                         }
    376                 }
    377                 if (!edit) {
    378                         for (int i = 0; i < propertyData.getRowCount(); ++i)
    379                                 allData.remove(propertyData.getValueAt(i, 0));
    380                 }
    381                 return allData;
    382         }
    383 
    384         /**
    385         * Delete the keys from the given row.
    386          * @param row   The row, which key gets deleted from the dataset.
    387         */
    388         private void delete(int row) {
    389                 String key = propertyData.getValueAt(row, 0).toString();
    390                 Collection<OsmPrimitive> sel = Main.ds.getSelected();
    391                 Main.main.undoRedo.add(new ChangePropertyCommand(sel, key, null));
    392                 Main.ds.fireSelectionChanged(sel);
    393                 selectionChanged(sel); // update table
    394         }
    395 
    396         /**
    397         * The property data.
    398         */
    399         private final DefaultTableModel propertyData = new DefaultTableModel() {
    400                 @Override public boolean isCellEditable(int row, int column) {
    401                         return false;
    402                 }
    403                 @Override public Class<?> getColumnClass(int columnIndex) {
    404                         return String.class;
    405                 }
    406         };
    407 
    408         /**
    409         * The membership data.
    410         */
    411         private final DefaultTableModel membershipData = new DefaultTableModel() {
    412                 @Override public boolean isCellEditable(int row, int column) {
    413                         return false;
    414                 }
    415                 @Override public Class<?> getColumnClass(int columnIndex) {
    416                         return String.class;
    417                 }
    418         };
    419 
    420         /**
    421         * The properties list.
    422         */
    423         private final JTable propertyTable = new JTable(propertyData);
    424         private final JTable membershipTable = new JTable(membershipData);
    425 
    426         public JComboBox taggingPresets = new JComboBox();
    427 
    428 
    429         /**
    430         * Create a new PropertiesDialog
    431         */
    432         public PropertiesDialog(MapFrame mapFrame) {
    433                 super(tr("Properties/Memberships"), "propertiesdialog", tr("Properties for selected objects."),
    434                 Shortcut.registerShortcut("subwindow:properties", tr("Toggle: {0}", tr("Properties/Memberships")), KeyEvent.VK_P,
    435                 Shortcut.GROUP_LAYER, Shortcut.SHIFT_DEFAULT), 150);
    436 
    437                 // ---------------------------------------
    438                 // This drop-down is really deprecated but we offer people a chance to
    439                 // activate it if they really want. Presets should be used from the
    440                 // menu.
    441                 if (TaggingPresetPreference.taggingPresets.size() > 0 &&
    442                                 Main.pref.getBoolean("taggingpreset.in-properties-dialog", false)) {
    443                         Vector<ActionListener> allPresets = new Vector<ActionListener>();
    444                         for (final TaggingPreset p : TaggingPresetPreference.taggingPresets)
    445                                 allPresets.add(new ForwardActionListener(this, p));
    446 
    447                         TaggingPreset empty = new TaggingPreset();
    448                         // empty.setName("this drop-down will be removed soon");
    449                         allPresets.add(0, new ForwardActionListener(this, empty));
    450                         taggingPresets.setModel(new DefaultComboBoxModel(allPresets));
    451                         JPanel north = new JPanel(new GridBagLayout());
    452                         north.add(getComponent(0),GBC.eol().fill(GBC.HORIZONTAL));
    453                         north.add(taggingPresets,GBC.eol().fill(GBC.HORIZONTAL));
    454                         add(north, BorderLayout.NORTH);
    455                 }
    456                 taggingPresets.addActionListener(new ActionListener(){
    457                         public void actionPerformed(ActionEvent e) {
    458                                 TaggingPreset preset = ((ForwardActionListener)taggingPresets.getSelectedItem()).preset;
    459                                 preset.actionPerformed(e);
    460                                 taggingPresets.setSelectedItem(null);
    461                         }
    462                 });
    463                 taggingPresets.setRenderer(new TaggingCellRenderer());
    464 
    465                 // setting up the properties table
    466 
    467                 propertyData.setColumnIdentifiers(new String[]{tr("Key"),tr("Value")});
    468                 propertyTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
    469 
    470                 propertyTable.getColumnModel().getColumn(1).setCellRenderer(new DefaultTableCellRenderer(){
    471                         @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
    472                                 Component c = super.getTableCellRendererComponent(table, value, isSelected, false, row, column);
    473                                 if (c instanceof JLabel) {
    474                                         String str = null;
    475                                         switch (column) {
    476                                         case 0:
    477                                                 str = (String) value;
    478                                         break;
    479                                         case 1:
    480                                                 Map<String, Integer> v = (Map<String,Integer>) value;
    481                                                 if (v.size()!=1) {
    482                                                         str=tr("<different>");
    483                                                         c.setFont(c.getFont().deriveFont(Font.ITALIC));
    484                                                 } else {
    485                                                         str=v.entrySet().iterator().next().getKey();
    486                                                 }
    487                                         break;
    488                                         }
    489                                         ((JLabel)c).setText(str);
    490                                 }
    491                                 return c;
    492                         }
    493                 });
    494 
    495                 // setting up the membership table
    496 
    497                 membershipData.setColumnIdentifiers(new String[]{tr("Member Of"),tr("Role")});
    498                 membershipTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
    499 
    500                 membershipTable.getColumnModel().getColumn(0).setCellRenderer(new DefaultTableCellRenderer() {
    501                         @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
    502                                 Component c = super.getTableCellRendererComponent(table, value, isSelected, false, row, column);
    503                                 if (c instanceof JLabel) {
    504                                         nameVisitor.visit((Relation)value);
    505                                         ((JLabel)c).setText(nameVisitor.name);
    506                                 }
    507                                 return c;
    508                         }
    509                 });
    510 
    511                 membershipTable.getColumnModel().getColumn(1).setCellRenderer(new DefaultTableCellRenderer() {
    512                         @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
    513                                 Component c = super.getTableCellRendererComponent(table, value, isSelected, false, row, column);
    514                                 if (c instanceof JLabel) {
    515                                         Collection<RelationMember> col = (Collection<RelationMember>) value;
    516 
    517                                         String text = null;
    518                                         for (RelationMember r : col) {
    519                                                 if (text == null) {
    520                                                         text = r.role;
    521                                                 }
    522                                                 else if (!text.equals(r.role)) {
    523                                                         text = tr("<different>");
    524                                                         break;
    525                                                 }
    526                                         }
    527 
    528                                         ((JLabel)c).setText(text);
    529                                 }
    530                                 return c;
    531                         }
    532                 });
    533 
    534                 // combine both tables and wrap them in a scrollPane
    535                 JPanel bothTables = new JPanel();
    536                 bothTables.setLayout(new GridBagLayout());
    537                 bothTables.add(propertyTable.getTableHeader(), GBC.eol().fill(GBC.HORIZONTAL));
    538                 bothTables.add(propertyTable, GBC.eol().fill(GBC.BOTH));
    539                 bothTables.add(membershipTable.getTableHeader(), GBC.eol().fill(GBC.HORIZONTAL));
    540                 bothTables.add(membershipTable, GBC.eol().fill(GBC.BOTH));
    541 
    542                 DblClickWatch dblClickWatch = new DblClickWatch();
    543                 propertyTable.addMouseListener(dblClickWatch);
    544                 membershipTable.addMouseListener(dblClickWatch);
    545                 JScrollPane scrollPane = new JScrollPane(bothTables);
    546                 scrollPane.addMouseListener(dblClickWatch);
    547                 add(scrollPane, BorderLayout.CENTER);
    548 
    549                 JPanel buttonPanel = new JPanel(new GridLayout(1,3));
    550                 ActionListener buttonAction = new ActionListener(){
    551                         public void actionPerformed(ActionEvent e) {
    552                                 int row = membershipTable.getSelectedRow();
    553                                 if (e.getActionCommand().equals("Add"))
    554                                         add();
    555                                 else if(row >= 0)
    556                                 {
    557                                         if (e.getActionCommand().equals("Edit"))
    558                                                 membershipEdit(row);
    559                                         else if (e.getActionCommand().equals("Delete")) {
    560                                                 Relation cur = (Relation)membershipData.getValueAt(row, 0);
    561                                                 NameVisitor n = new NameVisitor();
    562                                                 cur.visit(n);
    563                                                 if(JOptionPane.showConfirmDialog(Main.parent, tr("Really delete selection from relation {0}?", n.name),
    564                                                 tr("Change relation"), JOptionPane.YES_NO_OPTION) == JOptionPane.YES_NO_OPTION)
    565                                                 {
    566                                                         Relation rel = new Relation(cur);
    567                                                         Collection<OsmPrimitive> sel = Main.ds.getSelected();
    568                                                         for (RelationMember rm : cur.members) {
    569                                                                 for (OsmPrimitive osm : sel) {
    570                                                                         if (rm.member == osm)
    571                                                                         {
    572                                                                                 RelationMember mem = new RelationMember();
    573                                                                                 mem.role = rm.role;
    574                                                                                 mem.member = rm.member;
    575                                                                                 rel.members.remove(mem);
    576                                                                                 break;
    577                                                                         }
    578                                                                 }
    579                                                         }
    580                                                         Main.main.undoRedo.add(new ChangeCommand(cur, rel));
    581                                                         Main.ds.fireSelectionChanged(sel);
    582                                                         selectionChanged(sel); // update whole table
    583                                                 }
    584 
    585                                         }
    586                                 }
    587                                 else
    588                                 {
    589                                         int sel = propertyTable.getSelectedRow();
    590                                         if (e.getActionCommand().equals("Edit")) {
    591                                                 if(propertyTable.getRowCount() == 1)
    592                                                         sel = 0;
    593                                                 if (sel == -1)
    594                                                         JOptionPane.showMessageDialog(Main.parent, tr("Please select the row to edit."));
    595                                                 else
    596                                                         propertyEdit(sel);
    597                                         } else if (e.getActionCommand().equals("Delete")) {
    598                                                 if (sel == -1)
    599                                                         JOptionPane.showMessageDialog(Main.parent, tr("Please select the row to delete."));
    600                                                 else
    601                                                         delete(sel);
    602                                         }
    603                                 }
    604                         }
    605                 };
    606 
    607                 Shortcut s = Shortcut.registerShortcut("properties:add", tr("Add Properties"), KeyEvent.VK_B,
    608                 Shortcut.GROUP_MNEMONIC);
    609                 buttonPanel.add(new SideButton(marktr("Add"),"add","Properties",
     334        Main.main.undoRedo.add(new ChangePropertyCommand(sel, key, value));
     335        Main.ds.fireSelectionChanged(sel);
     336        selectionChanged(sel); // update table
     337        Main.parent.repaint(); // repaint all - drawing could have been changed
     338    }
     339
     340    /**
     341    * @param allData
     342    * @param keys
     343    * @param values
     344    */
     345    private void addFocusAdapter(final int row, final TreeMap<String, TreeSet<String>> allData,final AutoCompleteComboBox keys, final AutoCompleteComboBox values) {
     346        // get the combo box' editor component
     347        JTextComponent editor = (JTextComponent)values.getEditor()
     348                .getEditorComponent();
     349        // Refresh the values model when focus is gained
     350        editor.addFocusListener(new FocusAdapter() {
     351            @Override public void focusGained(FocusEvent e) {
     352                String key = keys.getEditor().getItem().toString();
     353                updateListData(key, allData, values);
     354                objKey=key;
     355            }
     356        });
     357    }
     358    private String objKey;
     359    /**
     360    * @return
     361    */
     362    private TreeMap<String, TreeSet<String>> createAutoCompletionInfo(
     363            boolean edit) {
     364        final TreeMap<String, TreeSet<String>> allData = new TreeMap<String, TreeSet<String>>();
     365        for (OsmPrimitive osm : Main.ds.allNonDeletedPrimitives()) {
     366            for (String key : osm.keySet()) {
     367                TreeSet<String> values = null;
     368                if (allData.containsKey(key))
     369                    values = allData.get(key);
     370                else {
     371                    values = new TreeSet<String>();
     372                    allData.put(key, values);
     373                }
     374                values.add(osm.get(key));
     375            }
     376        }
     377        if (!edit) {
     378            for (int i = 0; i < propertyData.getRowCount(); ++i)
     379                allData.remove(propertyData.getValueAt(i, 0));
     380        }
     381        return allData;
     382    }
     383
     384    /**
     385    * Delete the keys from the given row.
     386     * @param row   The row, which key gets deleted from the dataset.
     387    */
     388    private void delete(int row) {
     389        String key = propertyData.getValueAt(row, 0).toString();
     390        Collection<OsmPrimitive> sel = Main.ds.getSelected();
     391        Main.main.undoRedo.add(new ChangePropertyCommand(sel, key, null));
     392        Main.ds.fireSelectionChanged(sel);
     393        selectionChanged(sel); // update table
     394    }
     395
     396    /**
     397    * The property data.
     398    */
     399    private final DefaultTableModel propertyData = new DefaultTableModel() {
     400        @Override public boolean isCellEditable(int row, int column) {
     401            return false;
     402        }
     403        @Override public Class<?> getColumnClass(int columnIndex) {
     404            return String.class;
     405        }
     406    };
     407
     408    /**
     409    * The membership data.
     410    */
     411    private final DefaultTableModel membershipData = new DefaultTableModel() {
     412        @Override public boolean isCellEditable(int row, int column) {
     413            return false;
     414        }
     415        @Override public Class<?> getColumnClass(int columnIndex) {
     416            return String.class;
     417        }
     418    };
     419
     420    /**
     421    * The properties list.
     422    */
     423    private final JTable propertyTable = new JTable(propertyData);
     424    private final JTable membershipTable = new JTable(membershipData);
     425
     426    public JComboBox taggingPresets = new JComboBox();
     427
     428
     429    /**
     430    * Create a new PropertiesDialog
     431    */
     432    public PropertiesDialog(MapFrame mapFrame) {
     433        super(tr("Properties/Memberships"), "propertiesdialog", tr("Properties for selected objects."),
     434        Shortcut.registerShortcut("subwindow:properties", tr("Toggle: {0}", tr("Properties/Memberships")), KeyEvent.VK_P,
     435        Shortcut.GROUP_LAYER, Shortcut.SHIFT_DEFAULT), 150);
     436
     437        // ---------------------------------------
     438        // This drop-down is really deprecated but we offer people a chance to
     439        // activate it if they really want. Presets should be used from the
     440        // menu.
     441        if (TaggingPresetPreference.taggingPresets.size() > 0 &&
     442                Main.pref.getBoolean("taggingpreset.in-properties-dialog", false)) {
     443            Vector<ActionListener> allPresets = new Vector<ActionListener>();
     444            for (final TaggingPreset p : TaggingPresetPreference.taggingPresets)
     445                allPresets.add(new ForwardActionListener(this, p));
     446
     447            TaggingPreset empty = new TaggingPreset();
     448            // empty.setName("this drop-down will be removed soon");
     449            allPresets.add(0, new ForwardActionListener(this, empty));
     450            taggingPresets.setModel(new DefaultComboBoxModel(allPresets));
     451            JPanel north = new JPanel(new GridBagLayout());
     452            north.add(getComponent(0),GBC.eol().fill(GBC.HORIZONTAL));
     453            north.add(taggingPresets,GBC.eol().fill(GBC.HORIZONTAL));
     454            add(north, BorderLayout.NORTH);
     455        }
     456        taggingPresets.addActionListener(new ActionListener(){
     457            public void actionPerformed(ActionEvent e) {
     458                TaggingPreset preset = ((ForwardActionListener)taggingPresets.getSelectedItem()).preset;
     459                preset.actionPerformed(e);
     460                taggingPresets.setSelectedItem(null);
     461            }
     462        });
     463        taggingPresets.setRenderer(new TaggingCellRenderer());
     464
     465        // setting up the properties table
     466
     467        propertyData.setColumnIdentifiers(new String[]{tr("Key"),tr("Value")});
     468        propertyTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
     469
     470        propertyTable.getColumnModel().getColumn(1).setCellRenderer(new DefaultTableCellRenderer(){
     471            @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
     472                Component c = super.getTableCellRendererComponent(table, value, isSelected, false, row, column);
     473                if (c instanceof JLabel) {
     474                    String str = null;
     475                    switch (column) {
     476                    case 0:
     477                        str = (String) value;
     478                    break;
     479                    case 1:
     480                        Map<String, Integer> v = (Map<String,Integer>) value;
     481                        if (v.size()!=1) {
     482                            str=tr("<different>");
     483                            c.setFont(c.getFont().deriveFont(Font.ITALIC));
     484                        } else {
     485                            str=v.entrySet().iterator().next().getKey();
     486                        }
     487                    break;
     488                    }
     489                    ((JLabel)c).setText(str);
     490                }
     491                return c;
     492            }
     493        });
     494
     495        // setting up the membership table
     496
     497        membershipData.setColumnIdentifiers(new String[]{tr("Member Of"),tr("Role")});
     498        membershipTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
     499
     500        membershipTable.getColumnModel().getColumn(0).setCellRenderer(new DefaultTableCellRenderer() {
     501            @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
     502                Component c = super.getTableCellRendererComponent(table, value, isSelected, false, row, column);
     503                if (c instanceof JLabel) {
     504                    nameVisitor.visit((Relation)value);
     505                    ((JLabel)c).setText(nameVisitor.name);
     506                }
     507                return c;
     508            }
     509        });
     510
     511        membershipTable.getColumnModel().getColumn(1).setCellRenderer(new DefaultTableCellRenderer() {
     512            @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
     513                Component c = super.getTableCellRendererComponent(table, value, isSelected, false, row, column);
     514                if (c instanceof JLabel) {
     515                    Collection<RelationMember> col = (Collection<RelationMember>) value;
     516
     517                    String text = null;
     518                    for (RelationMember r : col) {
     519                        if (text == null) {
     520                            text = r.role;
     521                        }
     522                        else if (!text.equals(r.role)) {
     523                            text = tr("<different>");
     524                            break;
     525                        }
     526                    }
     527
     528                    ((JLabel)c).setText(text);
     529                }
     530                return c;
     531            }
     532        });
     533
     534        // combine both tables and wrap them in a scrollPane
     535        JPanel bothTables = new JPanel();
     536        bothTables.setLayout(new GridBagLayout());
     537        bothTables.add(propertyTable.getTableHeader(), GBC.eol().fill(GBC.HORIZONTAL));
     538        bothTables.add(propertyTable, GBC.eol().fill(GBC.BOTH));
     539        bothTables.add(membershipTable.getTableHeader(), GBC.eol().fill(GBC.HORIZONTAL));
     540        bothTables.add(membershipTable, GBC.eol().fill(GBC.BOTH));
     541
     542        DblClickWatch dblClickWatch = new DblClickWatch();
     543        propertyTable.addMouseListener(dblClickWatch);
     544        membershipTable.addMouseListener(dblClickWatch);
     545        JScrollPane scrollPane = new JScrollPane(bothTables);
     546        scrollPane.addMouseListener(dblClickWatch);
     547        add(scrollPane, BorderLayout.CENTER);
     548
     549        JPanel buttonPanel = new JPanel(new GridLayout(1,3));
     550        ActionListener buttonAction = new ActionListener(){
     551            public void actionPerformed(ActionEvent e) {
     552                int row = membershipTable.getSelectedRow();
     553                if (e.getActionCommand().equals("Add"))
     554                    add();
     555                else if(row >= 0)
     556                {
     557                    if (e.getActionCommand().equals("Edit"))
     558                        membershipEdit(row);
     559                    else if (e.getActionCommand().equals("Delete")) {
     560                        Relation cur = (Relation)membershipData.getValueAt(row, 0);
     561                        NameVisitor n = new NameVisitor();
     562                        cur.visit(n);
     563                        if(JOptionPane.showConfirmDialog(Main.parent, tr("Really delete selection from relation {0}?", n.name),
     564                        tr("Change relation"), JOptionPane.YES_NO_OPTION) == JOptionPane.YES_NO_OPTION)
     565                        {
     566                            Relation rel = new Relation(cur);
     567                            Collection<OsmPrimitive> sel = Main.ds.getSelected();
     568                            for (RelationMember rm : cur.members) {
     569                                for (OsmPrimitive osm : sel) {
     570                                    if (rm.member == osm)
     571                                    {
     572                                        RelationMember mem = new RelationMember();
     573                                        mem.role = rm.role;
     574                                        mem.member = rm.member;
     575                                        rel.members.remove(mem);
     576                                        break;
     577                                    }
     578                                }
     579                            }
     580                            Main.main.undoRedo.add(new ChangeCommand(cur, rel));
     581                            Main.ds.fireSelectionChanged(sel);
     582                            selectionChanged(sel); // update whole table
     583                        }
     584
     585                    }
     586                }
     587                else
     588                {
     589                    int sel = propertyTable.getSelectedRow();
     590                    if (e.getActionCommand().equals("Edit")) {
     591                        if(propertyTable.getRowCount() == 1)
     592                            sel = 0;
     593                        if (sel == -1)
     594                            JOptionPane.showMessageDialog(Main.parent, tr("Please select the row to edit."));
     595                        else
     596                            propertyEdit(sel);
     597                    } else if (e.getActionCommand().equals("Delete")) {
     598                        if (sel == -1)
     599                            JOptionPane.showMessageDialog(Main.parent, tr("Please select the row to delete."));
     600                        else
     601                            delete(sel);
     602                    }
     603                }
     604            }
     605        };
     606
     607        Shortcut s = Shortcut.registerShortcut("properties:add", tr("Add Properties"), KeyEvent.VK_B,
     608        Shortcut.GROUP_MNEMONIC);
     609        buttonPanel.add(new SideButton(marktr("Add"),"add","Properties",
    610610                tr("Add a new key/value pair to all objects"), s, buttonAction));
    611611
    612                 s = Shortcut.registerShortcut("properties:edit", tr("Edit Properties"), KeyEvent.VK_I,
    613                 Shortcut.GROUP_MNEMONIC);
    614                 buttonPanel.add(new SideButton(marktr("Edit"),"edit","Properties",
     612        s = Shortcut.registerShortcut("properties:edit", tr("Edit Properties"), KeyEvent.VK_I,
     613        Shortcut.GROUP_MNEMONIC);
     614        buttonPanel.add(new SideButton(marktr("Edit"),"edit","Properties",
    615615                tr("Edit the value of the selected key for all objects"), s, buttonAction));
    616616
    617                 s = Shortcut.registerShortcut("properties:delete", tr("Delete Properties"), KeyEvent.VK_Q,
    618                 Shortcut.GROUP_MNEMONIC);
    619                 buttonPanel.add(new SideButton(marktr("Delete"),"delete","Properties",
     617        s = Shortcut.registerShortcut("properties:delete", tr("Delete Properties"), KeyEvent.VK_Q,
     618        Shortcut.GROUP_MNEMONIC);
     619        buttonPanel.add(new SideButton(marktr("Delete"),"delete","Properties",
    620620                tr("Delete the selected key in all objects"), s, buttonAction));
    621                 add(buttonPanel, BorderLayout.SOUTH);
    622 
    623                 DataSet.selListeners.add(this);
    624         }
    625 
    626         @Override public void setVisible(boolean b) {
    627                 super.setVisible(b);
    628                 if (b)
    629                         selectionChanged(Main.ds.getSelected());
    630         }
    631 
    632         public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) {
    633                 if (!isVisible())
    634                         return;
    635                 if (propertyTable == null)
    636                         return; // selection changed may be received in base class constructor before init
    637                 if (propertyTable.getCellEditor() != null)
    638                         propertyTable.getCellEditor().cancelCellEditing();
    639 
    640                 // re-load property data
    641 
    642                 propertyData.setRowCount(0);
    643 
    644                 Map<String, Integer> keyCount = new HashMap<String, Integer>();
    645                 valueCount.clear();
    646                 for (OsmPrimitive osm : newSelection) {
    647                         for (Entry<String, String> e : osm.entrySet()) {
    648                                 keyCount.put(e.getKey(), keyCount.containsKey(e.getKey()) ? keyCount.get(e.getKey())+1 : 1);
    649                                 if (valueCount.containsKey(e.getKey())) {
    650                                         Map<String, Integer> v = valueCount.get(e.getKey());
    651                                         v.put(e.getValue(), v.containsKey(e.getValue())? v.get(e.getValue())+1 : 1 );
    652                                 } else {
    653                                         TreeMap<String,Integer> v = new TreeMap<String, Integer>();
    654                                         v.put(e.getValue(), 1);
    655                                         valueCount.put(e.getKey(), v);
    656                                 }
    657                         }
    658                 }
    659                 for (Entry<String, Map<String, Integer>> e : valueCount.entrySet()) {
    660                         int count=0;
    661                         for (Entry<String, Integer> e1: e.getValue().entrySet()) {
    662                                 count+=e1.getValue();
    663                         }
    664                         if (count < newSelection.size()) {
    665                                 e.getValue().put("", newSelection.size()-count);
    666                         }
    667                         propertyData.addRow(new Object[]{e.getKey(), e.getValue()});
    668                 }
    669 
    670                 // re-load membership data
    671                 // this is rather expensive since we have to walk through all members of all existing relationships.
    672                 // could use back references here for speed if necessary.
    673 
    674                 membershipData.setRowCount(0);
    675 
    676                 Map<Relation, Collection<RelationMember>> roles = new HashMap<Relation, Collection<RelationMember>>();
    677                 for (Relation r : Main.ds.relations) {
    678                         if (!r.deleted && !r.incomplete) {
    679                                 for (RelationMember m : r.members) {
    680                                         if (newSelection.contains(m.member)) {
    681                                                 Collection<RelationMember> value = roles.get(r);
    682                                                 if (value == null) {
    683                                                         value = new HashSet<RelationMember>();
    684                                                         roles.put(r, value);
    685                                                 }
    686                                                 value.add(m);
    687                                         }
    688                                 }
    689                         }
    690                 }
    691 
    692                 for (Entry<Relation, Collection<RelationMember>> e : roles.entrySet()) {
    693                         membershipData.addRow(new Object[]{e.getKey(), e.getValue()});
    694                 }
    695 
    696                 membershipTable.getTableHeader().setVisible(membershipData.getRowCount() > 0);
    697         }
     621        add(buttonPanel, BorderLayout.SOUTH);
     622
     623        DataSet.selListeners.add(this);
     624    }
     625
     626    @Override public void setVisible(boolean b) {
     627        super.setVisible(b);
     628        if (b)
     629            selectionChanged(Main.ds.getSelected());
     630    }
     631
     632    public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) {
     633        if (!isVisible())
     634            return;
     635        if (propertyTable == null)
     636            return; // selection changed may be received in base class constructor before init
     637        if (propertyTable.getCellEditor() != null)
     638            propertyTable.getCellEditor().cancelCellEditing();
     639
     640        // re-load property data
     641
     642        propertyData.setRowCount(0);
     643
     644        Map<String, Integer> keyCount = new HashMap<String, Integer>();
     645        valueCount.clear();
     646        for (OsmPrimitive osm : newSelection) {
     647            for (Entry<String, String> e : osm.entrySet()) {
     648                keyCount.put(e.getKey(), keyCount.containsKey(e.getKey()) ? keyCount.get(e.getKey())+1 : 1);
     649                if (valueCount.containsKey(e.getKey())) {
     650                    Map<String, Integer> v = valueCount.get(e.getKey());
     651                    v.put(e.getValue(), v.containsKey(e.getValue())? v.get(e.getValue())+1 : 1 );
     652                } else {
     653                    TreeMap<String,Integer> v = new TreeMap<String, Integer>();
     654                    v.put(e.getValue(), 1);
     655                    valueCount.put(e.getKey(), v);
     656                }
     657            }
     658        }
     659        for (Entry<String, Map<String, Integer>> e : valueCount.entrySet()) {
     660            int count=0;
     661            for (Entry<String, Integer> e1: e.getValue().entrySet()) {
     662                count+=e1.getValue();
     663            }
     664            if (count < newSelection.size()) {
     665                e.getValue().put("", newSelection.size()-count);
     666            }
     667            propertyData.addRow(new Object[]{e.getKey(), e.getValue()});
     668        }
     669
     670        // re-load membership data
     671        // this is rather expensive since we have to walk through all members of all existing relationships.
     672        // could use back references here for speed if necessary.
     673
     674        membershipData.setRowCount(0);
     675
     676        Map<Relation, Collection<RelationMember>> roles = new HashMap<Relation, Collection<RelationMember>>();
     677        for (Relation r : Main.ds.relations) {
     678            if (!r.deleted && !r.incomplete) {
     679                for (RelationMember m : r.members) {
     680                    if (newSelection.contains(m.member)) {
     681                        Collection<RelationMember> value = roles.get(r);
     682                        if (value == null) {
     683                            value = new HashSet<RelationMember>();
     684                            roles.put(r, value);
     685                        }
     686                        value.add(m);
     687                    }
     688                }
     689            }
     690        }
     691
     692        for (Entry<Relation, Collection<RelationMember>> e : roles.entrySet()) {
     693            membershipData.addRow(new Object[]{e.getKey(), e.getValue()});
     694        }
     695
     696        membershipTable.getTableHeader().setVisible(membershipData.getRowCount() > 0);
     697    }
    698698}
  • trunk/src/org/openstreetmap/josm/gui/dialogs/RelationEditor.java

    r1146 r1169  
    6565public class RelationEditor extends JFrame {
    6666
    67         /**
    68         * The relation that this editor is working on, and the clone made for
    69         * editing.
    70         */
    71         private final Relation relation;
    72         private final Relation clone;
    73         private JLabel status;
     67    /**
     68    * The relation that this editor is working on, and the clone made for
     69    * editing.
     70    */
     71    private final Relation relation;
     72    private final Relation clone;
     73    private JLabel status;
    7474
    7575    /**
     
    7878    boolean ordered;
    7979
    80         /**
    81         * The property data.
    82         */
    83         private final DefaultTableModel propertyData = new DefaultTableModel() {
    84                 @Override public boolean isCellEditable(int row, int column) {
    85                         return true;
    86                 }
    87                 @Override public Class<?> getColumnClass(int columnIndex) {
    88                         return String.class;
    89                 }
    90         };
    91 
    92         /**
    93         * The membership data.
    94         */
    95         private final DefaultTableModel memberData = new DefaultTableModel() {
    96                 @Override public boolean isCellEditable(int row, int column) {
    97                         return column == 0;
    98                 }
    99                 @Override public Class<?> getColumnClass(int columnIndex) {
    100                         return columnIndex == 1 ? OsmPrimitive.class : String.class;
    101                 }
    102         };
    103 
    104         /**
    105         * The properties and membership lists.
    106         */
    107         private final JTable propertyTable = new JTable(propertyData);
    108         private final JTable memberTable = new JTable(memberData);
     80    /**
     81    * The property data.
     82    */
     83    private final DefaultTableModel propertyData = new DefaultTableModel() {
     84        @Override public boolean isCellEditable(int row, int column) {
     85            return true;
     86        }
     87        @Override public Class<?> getColumnClass(int columnIndex) {
     88            return String.class;
     89        }
     90    };
     91
     92    /**
     93    * The membership data.
     94    */
     95    private final DefaultTableModel memberData = new DefaultTableModel() {
     96        @Override public boolean isCellEditable(int row, int column) {
     97            return column == 0;
     98        }
     99        @Override public Class<?> getColumnClass(int columnIndex) {
     100            return columnIndex == 1 ? OsmPrimitive.class : String.class;
     101        }
     102    };
     103
     104    /**
     105    * The properties and membership lists.
     106    */
     107    private final JTable propertyTable = new JTable(propertyData);
     108    private final JTable memberTable = new JTable(memberData);
    109109
    110110    // =================== FIXME FIXME FIXME =====================
    111111    // As soon as API 0.5 is dead, drop all the collation stuff from here ...
    112    
    113         /**
    114         * Collator for sorting the roles and entries of the member table.
    115         */
    116         private static final Collator collator;
    117         static {
    118                 collator = Collator.getInstance();
    119                 collator.setStrength(Collator.PRIMARY);
    120         }
    121 
    122         /**
    123         * Compare role strings.
    124         */
    125         private static int compareRole(String s1, String s2) {
    126                 int last1 = s1.lastIndexOf('_');
    127                 if (last1 > 0) {
    128                         int last2 = s2.lastIndexOf('_');
    129                         if (last2 == last1) {
    130                                 String prefix1 = s1.substring(0, last1);
    131                                 String prefix2 = s2.substring(0, last2);
    132 
    133                                 if (prefix1.equalsIgnoreCase(prefix2)) {
    134                                         // Both roles have the same prefix, now determine the
    135                                         // suffix.
    136                                         String suffix1 = s1.substring(last1 + 1, s1.length());
    137                                         String suffix2 = s2.substring(last2 + 1, s2.length());
    138 
    139                                         if (suffix1.matches("\\d+") && suffix2.matches("\\d+")) {
    140                                                 // Suffix is an number -> compare it.
    141                                                 int i1 = Integer.parseInt(suffix1);
    142                                                 int i2 = Integer.parseInt(suffix2);
    143 
    144                                                 return i1 - i2;
    145                                         }
    146                                 }
    147                         }
    148                 }
    149                 if(s1.length() == 0 && s2.length() != 0)
    150                         return 1;
    151                 else if(s2.length() == 0 && s1.length() != 0)
    152                         return -1;
    153 
    154                 // Default handling if the role name is nothing like "stop_xx"
    155                 return collator.compare(s1, s2);
    156         }
    157 
    158 
    159         /**
    160         * Compare two OsmPrimitives.
    161         */
    162         private static int compareMemebers(OsmPrimitive o1, OsmPrimitive o2) {
    163                 return collator.compare(o1.getName(), o2.getName());
    164         }
    165 
    166         private final Comparator<RelationMember> memberComparator = new Comparator<RelationMember>() {
    167                 public int compare(RelationMember r1, RelationMember r2) {
    168                         int roleResult = compareRole(r1.role, r2.role);
    169 
    170                         if (roleResult == 0)
    171                                 roleResult = compareMemebers(r1.member, r2.member);
    172 
    173                         return roleResult;
    174                 }
    175         };
     112
     113    /**
     114    * Collator for sorting the roles and entries of the member table.
     115    */
     116    private static final Collator collator;
     117    static {
     118        collator = Collator.getInstance();
     119        collator.setStrength(Collator.PRIMARY);
     120    }
     121
     122    /**
     123    * Compare role strings.
     124    */
     125    private static int compareRole(String s1, String s2) {
     126        int last1 = s1.lastIndexOf('_');
     127        if (last1 > 0) {
     128            int last2 = s2.lastIndexOf('_');
     129            if (last2 == last1) {
     130                String prefix1 = s1.substring(0, last1);
     131                String prefix2 = s2.substring(0, last2);
     132
     133                if (prefix1.equalsIgnoreCase(prefix2)) {
     134                    // Both roles have the same prefix, now determine the
     135                    // suffix.
     136                    String suffix1 = s1.substring(last1 + 1, s1.length());
     137                    String suffix2 = s2.substring(last2 + 1, s2.length());
     138
     139                    if (suffix1.matches("\\d+") && suffix2.matches("\\d+")) {
     140                        // Suffix is an number -> compare it.
     141                        int i1 = Integer.parseInt(suffix1);
     142                        int i2 = Integer.parseInt(suffix2);
     143
     144                        return i1 - i2;
     145                    }
     146                }
     147            }
     148        }
     149        if(s1.length() == 0 && s2.length() != 0)
     150            return 1;
     151        else if(s2.length() == 0 && s1.length() != 0)
     152            return -1;
     153
     154        // Default handling if the role name is nothing like "stop_xx"
     155        return collator.compare(s1, s2);
     156    }
     157
     158
     159    /**
     160    * Compare two OsmPrimitives.
     161    */
     162    private static int compareMemebers(OsmPrimitive o1, OsmPrimitive o2) {
     163        return collator.compare(o1.getName(), o2.getName());
     164    }
     165
     166    private final Comparator<RelationMember> memberComparator = new Comparator<RelationMember>() {
     167        public int compare(RelationMember r1, RelationMember r2) {
     168            int roleResult = compareRole(r1.role, r2.role);
     169
     170            if (roleResult == 0)
     171                roleResult = compareMemebers(r1.member, r2.member);
     172
     173            return roleResult;
     174        }
     175    };
    176176
    177177    // =================== FIXME FIXME FIXME =====================
    178178    // ... until here, and also get rid of the "Collections.sort..." below.
    179    
    180         /**
    181         * Creates a new relation editor for the given relation. The relation
    182         * will be saved if the user selects "ok" in the editor.
    183          *
    184         * If no relation is given, will create an editor for a new relation.
    185          *
    186         * @param relation relation to edit, or null to create a new one.
    187         */
    188         public RelationEditor(Relation relation)
    189         {
    190                 this(relation, null);
    191         }
    192 
    193         /**
    194         * Creates a new relation editor for the given relation. The relation
    195         * will be saved if the user selects "ok" in the editor.
    196          *
    197         * If no relation is given, will create an editor for a new relation.
    198          *
    199         * @param relation relation to edit, or null to create a new one.
    200         */
    201         public RelationEditor(Relation relation, Collection<RelationMember> selectedMembers )
    202         {
    203                 super(relation == null ? tr("Create new relation") :
    204                         relation.id == 0 ? tr ("Edit new relation") :
    205                         tr("Edit relation #{0}", relation.id));
    206                 this.relation = relation;
     179
     180    /**
     181    * Creates a new relation editor for the given relation. The relation
     182    * will be saved if the user selects "ok" in the editor.
     183     *
     184    * If no relation is given, will create an editor for a new relation.
     185     *
     186    * @param relation relation to edit, or null to create a new one.
     187    */
     188    public RelationEditor(Relation relation)
     189    {
     190        this(relation, null);
     191    }
     192
     193    /**
     194    * Creates a new relation editor for the given relation. The relation
     195    * will be saved if the user selects "ok" in the editor.
     196     *
     197    * If no relation is given, will create an editor for a new relation.
     198     *
     199    * @param relation relation to edit, or null to create a new one.
     200    */
     201    public RelationEditor(Relation relation, Collection<RelationMember> selectedMembers )
     202    {
     203        super(relation == null ? tr("Create new relation") :
     204            relation.id == 0 ? tr ("Edit new relation") :
     205            tr("Edit relation #{0}", relation.id));
     206        this.relation = relation;
    207207
    208208        ordered = Main.pref.get("osm-server.version", "0.5").equals("0.6");
    209        
    210                 if (relation == null) {
    211                         // create a new relation
    212                         this.clone = new Relation();
    213                 } else {
    214                         // edit an existing relation
    215                         this.clone = new Relation(relation);
    216                         if (!ordered) Collections.sort(this.clone.members, memberComparator);
    217                 }
    218 
    219                 getContentPane().setLayout(new BorderLayout());
    220                 JTabbedPane tabPane = new JTabbedPane();
    221                 getContentPane().add(tabPane, BorderLayout.CENTER);
    222 
    223                 // (ab)use JOptionPane to make this look familiar;
    224                 // hook up with JOptionPane's property change event
    225                 // to detect button click
    226                 final JOptionPane okcancel = new JOptionPane("",
    227                         JOptionPane.PLAIN_MESSAGE, JOptionPane.OK_CANCEL_OPTION, null);
    228                 getContentPane().add(okcancel, BorderLayout.SOUTH);
    229 
    230                 okcancel.addPropertyChangeListener(new PropertyChangeListener() {
    231                         public void propertyChange(PropertyChangeEvent event) {
    232                                 if (event.getPropertyName().equals(JOptionPane.VALUE_PROPERTY) && event.getNewValue() != null) {
    233                                         if ((Integer)event.getNewValue() == JOptionPane.OK_OPTION) {
    234                                                 // clicked ok!
    235                                                 if (RelationEditor.this.relation == null) {
    236                                                         Main.main.undoRedo.add(new AddCommand(clone));
    237                                                         DataSet.fireSelectionChanged(Main.ds.getSelected());
    238                                                 } else if (!RelationEditor.this.relation.realEqual(clone, true)) {
    239                                                         Main.main.undoRedo.add(new ChangeCommand(RelationEditor.this.relation, clone));
    240                                                         DataSet.fireSelectionChanged(Main.ds.getSelected());
    241                                                 }
    242                                         }
    243                                         setVisible(false);
    244                                 }
    245                         }
    246                 });
    247 
    248                 JLabel help = new JLabel("<html><em>"+
    249                         tr("This is the basic relation editor which allows you to change the relation's tags " +
    250                         "as well as the members. In addition to this we should have a smart editor that " +
    251                         "detects the type of relationship and limits your choices in a sensible way.")+"</em></html>");
    252                 getContentPane().add(help, BorderLayout.NORTH);
    253                 try { setAlwaysOnTop(true); } catch (SecurityException sx) {}
    254 
    255                 // Basic Editor panel has two blocks;
    256                 // a tag table at the top and a membership list below.
    257 
    258                 // setting up the properties table
    259 
    260                 propertyData.setColumnIdentifiers(new String[]{tr("Key"),tr("Value")});
    261                 propertyTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
    262                 propertyData.addTableModelListener(new TableModelListener() {
    263                         public void tableChanged(TableModelEvent tme) {
    264                                 if (tme.getType() == TableModelEvent.UPDATE) {
    265                                         int row = tme.getFirstRow();
    266 
    267                                         if (!(tme.getColumn() == 0 && row == propertyData.getRowCount() -1)) {
    268                                                 clone.entrySet().clear();
    269                                                 for (int i = 0; i < propertyData.getRowCount(); i++) {
    270                                                         String key = propertyData.getValueAt(i, 0).toString();
    271                                                         String value = propertyData.getValueAt(i, 1).toString();
    272                                                         if (key.length() > 0 && value.length() > 0) clone.put(key, value);
    273                                                 }
    274                                                 refreshTables();
    275                                         }
    276                                 }
    277                         }
    278                 });
    279                 propertyTable.putClientProperty("terminateEditOnFocusLost", Boolean.TRUE);
    280 
    281                 // setting up the member table
    282 
    283                 memberData.setColumnIdentifiers(new String[]{tr("Role"),tr("Occupied By")});
    284                 memberTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
    285                 memberTable.getColumnModel().getColumn(1).setCellRenderer(new OsmPrimitivRenderer());
    286                 memberData.addTableModelListener(new TableModelListener() {
    287                         public void tableChanged(TableModelEvent tme) {
    288                                 if (tme.getType() == TableModelEvent.UPDATE && tme.getColumn() == 0) {
    289                                         int row = tme.getFirstRow();
    290                                         clone.members.get(row).role = memberData.getValueAt(row, 0).toString();
    291                                 }
    292                         }
    293                 });
    294                 memberTable.putClientProperty("terminateEditOnFocusLost", Boolean.TRUE);
    295 
    296                 // combine both tables and wrap them in a scrollPane
    297                 JPanel bothTables = new JPanel();
    298                 bothTables.setLayout(new GridBagLayout());
    299                 bothTables.add(new JLabel(tr("Tags (empty value deletes tag)")), GBC.eol().fill(GBC.HORIZONTAL));
    300                 bothTables.add(new JScrollPane(propertyTable), GBC.eop().fill(GBC.BOTH));
    301                 bothTables.add(status = new JLabel(tr("Members")), GBC.eol().fill(GBC.HORIZONTAL));
     209
     210        if (relation == null) {
     211            // create a new relation
     212            this.clone = new Relation();
     213        } else {
     214            // edit an existing relation
     215            this.clone = new Relation(relation);
     216            if (!ordered) Collections.sort(this.clone.members, memberComparator);
     217        }
     218
     219        getContentPane().setLayout(new BorderLayout());
     220        JTabbedPane tabPane = new JTabbedPane();
     221        getContentPane().add(tabPane, BorderLayout.CENTER);
     222
     223        // (ab)use JOptionPane to make this look familiar;
     224        // hook up with JOptionPane's property change event
     225        // to detect button click
     226        final JOptionPane okcancel = new JOptionPane("",
     227            JOptionPane.PLAIN_MESSAGE, JOptionPane.OK_CANCEL_OPTION, null);
     228        getContentPane().add(okcancel, BorderLayout.SOUTH);
     229
     230        okcancel.addPropertyChangeListener(new PropertyChangeListener() {
     231            public void propertyChange(PropertyChangeEvent event) {
     232                if (event.getPropertyName().equals(JOptionPane.VALUE_PROPERTY) && event.getNewValue() != null) {
     233                    if ((Integer)event.getNewValue() == JOptionPane.OK_OPTION) {
     234                        // clicked ok!
     235                        if (RelationEditor.this.relation == null) {
     236                            Main.main.undoRedo.add(new AddCommand(clone));
     237                            DataSet.fireSelectionChanged(Main.ds.getSelected());
     238                        } else if (!RelationEditor.this.relation.realEqual(clone, true)) {
     239                            Main.main.undoRedo.add(new ChangeCommand(RelationEditor.this.relation, clone));
     240                            DataSet.fireSelectionChanged(Main.ds.getSelected());
     241                        }
     242                    }
     243                    setVisible(false);
     244                }
     245            }
     246        });
     247
     248        JLabel help = new JLabel("<html><em>"+
     249            tr("This is the basic relation editor which allows you to change the relation's tags " +
     250            "as well as the members. In addition to this we should have a smart editor that " +
     251            "detects the type of relationship and limits your choices in a sensible way.")+"</em></html>");
     252        getContentPane().add(help, BorderLayout.NORTH);
     253        try { setAlwaysOnTop(true); } catch (SecurityException sx) {}
     254
     255        // Basic Editor panel has two blocks;
     256        // a tag table at the top and a membership list below.
     257
     258        // setting up the properties table
     259
     260        propertyData.setColumnIdentifiers(new String[]{tr("Key"),tr("Value")});
     261        propertyTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
     262        propertyData.addTableModelListener(new TableModelListener() {
     263            public void tableChanged(TableModelEvent tme) {
     264                if (tme.getType() == TableModelEvent.UPDATE) {
     265                    int row = tme.getFirstRow();
     266
     267                    if (!(tme.getColumn() == 0 && row == propertyData.getRowCount() -1)) {
     268                        clone.entrySet().clear();
     269                        for (int i = 0; i < propertyData.getRowCount(); i++) {
     270                            String key = propertyData.getValueAt(i, 0).toString();
     271                            String value = propertyData.getValueAt(i, 1).toString();
     272                            if (key.length() > 0 && value.length() > 0) clone.put(key, value);
     273                        }
     274                        refreshTables();
     275                    }
     276                }
     277            }
     278        });
     279        propertyTable.putClientProperty("terminateEditOnFocusLost", Boolean.TRUE);
     280
     281        // setting up the member table
     282
     283        memberData.setColumnIdentifiers(new String[]{tr("Role"),tr("Occupied By")});
     284        memberTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
     285        memberTable.getColumnModel().getColumn(1).setCellRenderer(new OsmPrimitivRenderer());
     286        memberData.addTableModelListener(new TableModelListener() {
     287            public void tableChanged(TableModelEvent tme) {
     288                if (tme.getType() == TableModelEvent.UPDATE && tme.getColumn() == 0) {
     289                    int row = tme.getFirstRow();
     290                    clone.members.get(row).role = memberData.getValueAt(row, 0).toString();
     291                }
     292            }
     293        });
     294        memberTable.putClientProperty("terminateEditOnFocusLost", Boolean.TRUE);
     295
     296        // combine both tables and wrap them in a scrollPane
     297        JPanel bothTables = new JPanel();
     298        bothTables.setLayout(new GridBagLayout());
     299        bothTables.add(new JLabel(tr("Tags (empty value deletes tag)")), GBC.eol().fill(GBC.HORIZONTAL));
     300        bothTables.add(new JScrollPane(propertyTable), GBC.eop().fill(GBC.BOTH));
     301        bothTables.add(status = new JLabel(tr("Members")), GBC.eol().fill(GBC.HORIZONTAL));
    302302        if (ordered) {
    303303            JPanel upDownPanel = new JPanel();
    304304            upDownPanel.setLayout(new BoxLayout(upDownPanel, BoxLayout.Y_AXIS));
    305305
    306            
     306
    307307
    308308            upDownPanel.add(createButton(null, "moveup", tr("Move the currently selected member(s) up"),
     
    319319            }));
    320320
    321            
     321
    322322            bothTables.add(new JScrollPane(memberTable), GBC.std().fill(GBC.BOTH));
    323323            bothTables.add(upDownPanel, GBC.eol().fill(GBC.VERTICAL));
     
    325325            bothTables.add(new JScrollPane(memberTable), GBC.eol().fill(GBC.BOTH));
    326326        }
    327        
    328                 JPanel buttonPanel = new JPanel(new GridLayout(1,3));
    329 
    330                 buttonPanel.add(createButton(marktr("Add Selected"),"addselected",
    331                 tr("Add all currently selected objects as members"), KeyEvent.VK_A, new ActionListener() {
    332                         public void actionPerformed(ActionEvent e) {
    333                                 addSelected();
    334                         }
    335                 }));
    336 
    337                 buttonPanel.add(createButton(marktr("Delete Selected"),"deleteselected",
    338                 tr("Delete all currently selected objects from relation"), KeyEvent.VK_R, new ActionListener() {
    339                         public void actionPerformed(ActionEvent e) {
    340                                 deleteSelected();
    341                         }
    342                 }));
    343 
    344                 buttonPanel.add(createButton(marktr("Delete"),"delete",
    345                 tr("Remove the member in the current table row from this relation"), KeyEvent.VK_D, new ActionListener() {
    346                         public void actionPerformed(ActionEvent e) {
    347                                 int[] rows = memberTable.getSelectedRows();
    348                                 RelationMember mem = new RelationMember();
    349                                 for (int row : rows) {
    350                                         mem.role = memberTable.getValueAt(row, 0).toString();
    351                                         mem.member = (OsmPrimitive) memberTable.getValueAt(row, 1);
    352                                         clone.members.remove(mem);
    353                                 }
    354                                 refreshTables();
    355                         }
    356                 }));
    357 
    358                 buttonPanel.add(createButton(marktr("Select"),"select",
    359                 tr("Highlight the member from the current table row as JOSM's selection"), KeyEvent.VK_S, new ActionListener() {
    360                         public void actionPerformed(ActionEvent e) {
    361                                 ArrayList<OsmPrimitive> sel;
    362                                 int cnt = memberTable.getSelectedRowCount();
    363                                 if(cnt > 0)
    364                                 {
    365                                         sel = new ArrayList<OsmPrimitive>(cnt);
    366                                         for (int i : memberTable.getSelectedRows())
    367                                                 sel.add((OsmPrimitive)memberTable.getValueAt(i, 1));
    368                                 }
    369                                 else
    370                                 {
    371                                         cnt = memberTable.getRowCount();
    372                                         sel = new ArrayList<OsmPrimitive>(cnt);
    373                                         for (int i = 0; i < cnt; ++i)
    374                                                 sel.add((OsmPrimitive)memberTable.getValueAt(i, 1));
    375                                 }
    376                                 Main.ds.setSelected(sel);
    377                         }
    378                 }));
    379                 buttonPanel.add(createButton(marktr("Download Members"),"down",
    380                 tr("Download all incomplete ways and nodes in relation"), KeyEvent.VK_L, new ActionListener() {
    381                         public void actionPerformed(ActionEvent e) {
    382                                 downloadRelationMembers();
    383                                 refreshTables();
    384                         }
    385                 }));
    386 
    387                 bothTables.add(buttonPanel, GBC.eop().fill(GBC.HORIZONTAL));
    388 
    389                 tabPane.add(bothTables, "Basic");
    390 
    391                 refreshTables();
    392 
    393                 if (selectedMembers != null) {
    394                         boolean scrolled = false;
    395                         for (int i = 0; i < memberData.getRowCount(); i++) {           
    396                                 for (RelationMember m : selectedMembers) {
    397                                         if (m.member == memberData.getValueAt(i, 1)
    398                                                 && m.role.equals(memberData.getValueAt(i, 0))) {
    399                                                 memberTable.addRowSelectionInterval(i, i);
    400                                                 if (!scrolled) {
    401                                                         // Ensure that the first member is visible
    402                                                         memberTable.scrollRectToVisible(memberTable.getCellRect(i, 0, true));
    403                                                         scrolled = true;
    404                                                 }
    405                                                 break;
    406                                         }
    407                                 }
    408                                
    409                         }       
    410                 }
    411 
    412                 setSize(new Dimension(600, 500));
    413                 setLocationRelativeTo(Main.parent);
    414         }
    415 
    416         private void refreshTables() {
    417                 // re-load property data
    418 
    419                 propertyData.setRowCount(0);
    420                 for (Entry<String, String> e : clone.entrySet()) {
    421                         propertyData.addRow(new Object[]{e.getKey(), e.getValue()});
    422                 }
    423                 propertyData.addRow(new Object[]{"", ""});
    424 
    425                 // re-load membership data
    426 
    427                 memberData.setRowCount(0);
    428                 for (RelationMember em : clone.members) {
    429                         memberData.addRow(new Object[]{em.role, em.member});
    430                 }
    431                 status.setText(tr("Members: {0}", clone.members.size()));
    432         }
    433 
    434         private JButton createButton(String name, String iconName, String tooltip, int mnemonic, ActionListener actionListener) {
    435                 JButton b = new JButton(tr(name), ImageProvider.get("dialogs", iconName));
    436                 b.setActionCommand(name);
    437                 b.addActionListener(actionListener);
    438                 b.setToolTipText(tooltip);
    439                 b.setMnemonic(mnemonic);
    440                 b.putClientProperty("help", "Dialog/Properties/"+name);
    441                 return b;
    442         }
    443 
    444         private void addSelected() {
    445                 for (OsmPrimitive p : Main.ds.getSelected()) {
    446                         boolean skip = false;
     327
     328        JPanel buttonPanel = new JPanel(new GridLayout(1,3));
     329
     330        buttonPanel.add(createButton(marktr("Add Selected"),"addselected",
     331        tr("Add all currently selected objects as members"), KeyEvent.VK_A, new ActionListener() {
     332            public void actionPerformed(ActionEvent e) {
     333                addSelected();
     334            }
     335        }));
     336
     337        buttonPanel.add(createButton(marktr("Delete Selected"),"deleteselected",
     338        tr("Delete all currently selected objects from relation"), KeyEvent.VK_R, new ActionListener() {
     339            public void actionPerformed(ActionEvent e) {
     340                deleteSelected();
     341            }
     342        }));
     343
     344        buttonPanel.add(createButton(marktr("Delete"),"delete",
     345        tr("Remove the member in the current table row from this relation"), KeyEvent.VK_D, new ActionListener() {
     346            public void actionPerformed(ActionEvent e) {
     347                int[] rows = memberTable.getSelectedRows();
     348                RelationMember mem = new RelationMember();
     349                for (int row : rows) {
     350                    mem.role = memberTable.getValueAt(row, 0).toString();
     351                    mem.member = (OsmPrimitive) memberTable.getValueAt(row, 1);
     352                    clone.members.remove(mem);
     353                }
     354                refreshTables();
     355            }
     356        }));
     357
     358        buttonPanel.add(createButton(marktr("Select"),"select",
     359        tr("Highlight the member from the current table row as JOSM's selection"), KeyEvent.VK_S, new ActionListener() {
     360            public void actionPerformed(ActionEvent e) {
     361                ArrayList<OsmPrimitive> sel;
     362                int cnt = memberTable.getSelectedRowCount();
     363                if(cnt > 0)
     364                {
     365                    sel = new ArrayList<OsmPrimitive>(cnt);
     366                    for (int i : memberTable.getSelectedRows())
     367                        sel.add((OsmPrimitive)memberTable.getValueAt(i, 1));
     368                }
     369                else
     370                {
     371                    cnt = memberTable.getRowCount();
     372                    sel = new ArrayList<OsmPrimitive>(cnt);
     373                    for (int i = 0; i < cnt; ++i)
     374                        sel.add((OsmPrimitive)memberTable.getValueAt(i, 1));
     375                }
     376                Main.ds.setSelected(sel);
     377            }
     378        }));
     379        buttonPanel.add(createButton(marktr("Download Members"),"down",
     380        tr("Download all incomplete ways and nodes in relation"), KeyEvent.VK_L, new ActionListener() {
     381            public void actionPerformed(ActionEvent e) {
     382                downloadRelationMembers();
     383                refreshTables();
     384            }
     385        }));
     386
     387        bothTables.add(buttonPanel, GBC.eop().fill(GBC.HORIZONTAL));
     388
     389        tabPane.add(bothTables, "Basic");
     390
     391        refreshTables();
     392
     393        if (selectedMembers != null) {
     394            boolean scrolled = false;
     395            for (int i = 0; i < memberData.getRowCount(); i++) {
     396                for (RelationMember m : selectedMembers) {
     397                    if (m.member == memberData.getValueAt(i, 1)
     398                            && m.role.equals(memberData.getValueAt(i, 0))) {
     399                        memberTable.addRowSelectionInterval(i, i);
     400                        if (!scrolled) {
     401                            // Ensure that the first member is visible
     402                            memberTable.scrollRectToVisible(memberTable.getCellRect(i, 0, true));
     403                            scrolled = true;
     404                        }
     405                        break;
     406                    }
     407                }
     408
     409            }
     410        }
     411
     412        setSize(new Dimension(600, 500));
     413        setLocationRelativeTo(Main.parent);
     414    }
     415
     416    private void refreshTables() {
     417        // re-load property data
     418
     419        propertyData.setRowCount(0);
     420        for (Entry<String, String> e : clone.entrySet()) {
     421            propertyData.addRow(new Object[]{e.getKey(), e.getValue()});
     422        }
     423        propertyData.addRow(new Object[]{"", ""});
     424
     425        // re-load membership data
     426
     427        memberData.setRowCount(0);
     428        for (RelationMember em : clone.members) {
     429            memberData.addRow(new Object[]{em.role, em.member});
     430        }
     431        status.setText(tr("Members: {0}", clone.members.size()));
     432    }
     433
     434    private JButton createButton(String name, String iconName, String tooltip, int mnemonic, ActionListener actionListener) {
     435        JButton b = new JButton(tr(name), ImageProvider.get("dialogs", iconName));
     436        b.setActionCommand(name);
     437        b.addActionListener(actionListener);
     438        b.setToolTipText(tooltip);
     439        b.setMnemonic(mnemonic);
     440        b.putClientProperty("help", "Dialog/Properties/"+name);
     441        return b;
     442    }
     443
     444    private void addSelected() {
     445        for (OsmPrimitive p : Main.ds.getSelected()) {
     446            boolean skip = false;
    447447            // ordered relations may have the same member multiple times.
    448448            // TODO: visual indication of the fact that one is there more than once?
     
    457457                }
    458458            }
    459                         if (!skip)
    460                         {
    461                                 RelationMember em = new RelationMember();
    462                                 em.member = p;
    463                                 em.role = "";
     459            if (!skip)
     460            {
     461                RelationMember em = new RelationMember();
     462                em.member = p;
     463                em.role = "";
    464464                // when working with ordered relations, we make an effort to
    465465                // add the element before the first selected member.
     
    470470                    clone.members.add(em);
    471471                }
    472                         }
    473                 }
    474                 refreshTables();
    475         }
    476 
    477         private void deleteSelected() {
    478                 for (OsmPrimitive p : Main.ds.getSelected()) {
    479                         Relation c = new Relation(clone);
    480                         for (RelationMember rm : c.members) {
    481                                 if (rm.member == p)
    482                                 {
    483                                         RelationMember mem = new RelationMember();
    484                                         mem.role = rm.role;
    485                                         mem.member = rm.member;
    486                                         clone.members.remove(mem);
    487                                 }
    488                         }
    489                 }
    490                 refreshTables();
    491         }
    492 
    493         private void moveMembers(int direction) {
    494             int[] rows = memberTable.getSelectedRows();
     472            }
     473        }
     474        refreshTables();
     475    }
     476
     477    private void deleteSelected() {
     478        for (OsmPrimitive p : Main.ds.getSelected()) {
     479            Relation c = new Relation(clone);
     480            for (RelationMember rm : c.members) {
     481                if (rm.member == p)
     482                {
     483                    RelationMember mem = new RelationMember();
     484                    mem.role = rm.role;
     485                    mem.member = rm.member;
     486                    clone.members.remove(mem);
     487                }
     488            }
     489        }
     490        refreshTables();
     491    }
     492
     493    private void moveMembers(int direction) {
     494        int[] rows = memberTable.getSelectedRows();
    495495        if (rows.length == 0) return;
    496        
     496
    497497        // check if user attempted to move anything beyond the boundary of the list
    498498        if (rows[0] + direction < 0) return;
    499499        if (rows[rows.length-1] + direction >= clone.members.size()) return;
    500        
     500
    501501        RelationMember m[] = new RelationMember[clone.members.size()];
    502        
     502
    503503        // first move all selected rows from the member list into a new array,
    504504        // displaced by the move amount
    505505        for (Integer i: rows) {
    506506            m[i+direction] = clone.members.get(i);
    507             clone.members.set(i, null);           
    508         }
    509        
     507            clone.members.set(i, null);
     508        }
     509
    510510        // now fill the empty spots in the destination array with the remaining
    511511        // elements.
     
    517517            }
    518518        }
    519        
     519
    520520        // and write the array back into the member list.
    521521        clone.members.clear();
    522         clone.members.addAll(Arrays.asList(m));       
    523             refreshTables();
     522        clone.members.addAll(Arrays.asList(m));
     523        refreshTables();
    524524        ListSelectionModel lsm = memberTable.getSelectionModel();
    525525        lsm.setValueIsAdjusting(true);
     
    528528        }
    529529        lsm.setValueIsAdjusting(false);
    530         }
    531 
    532         private void downloadRelationMembers() {
    533                 boolean download = false;
    534                 for (RelationMember member : clone.members) {
    535                         if (member.member.incomplete) {
    536                                 download = true;
    537                                 break;
    538                         }
    539                 }
    540                 if (download) {
    541                         OsmServerObjectReader reader = new OsmServerObjectReader(clone.id, OsmServerObjectReader.TYPE_REL, true);
    542                         try {
    543                                 DataSet dataSet = reader.parseOsm();
    544                                 if (dataSet != null) {
    545                                         final MergeVisitor visitor = new MergeVisitor(Main.main
    546                                                         .editLayer().data, dataSet);
    547                                         for (final OsmPrimitive osm : dataSet.allPrimitives())
    548                                                 osm.visit(visitor);
    549                                         visitor.fixReferences();
    550 
    551                                         // copy the merged layer's data source info
    552                                         for (DataSource src : dataSet.dataSources)
    553                                                 Main.main.editLayer().data.dataSources.add(src);
    554                                         Main.main.editLayer().fireDataChange();
    555 
    556                                         if (visitor.conflicts.isEmpty())
    557                                                 return;
    558                                         final ConflictDialog dlg = Main.map.conflictDialog;
    559                                         dlg.add(visitor.conflicts);
    560                                         JOptionPane.showMessageDialog(Main.parent,
    561                                                         tr("There were conflicts during import."));
    562                                         if (!dlg.isVisible())
    563                                                 dlg.action
    564                                                                 .actionPerformed(new ActionEvent(this, 0, ""));
    565                                 }
    566 
    567                         } catch (SAXException e) {
    568                                 e.printStackTrace();
    569                                 JOptionPane.showMessageDialog(this,tr("Error parsing server response.")+": "+e.getMessage(),
    570                                 tr("Error"), JOptionPane.ERROR_MESSAGE);
    571                         } catch (IOException e) {
    572                                 e.printStackTrace();
    573                                 JOptionPane.showMessageDialog(this,tr("Cannot connect to server.")+": "+e.getMessage(),
    574                                 tr("Error"), JOptionPane.ERROR_MESSAGE);
    575                         }
    576                 }
    577         }
     530    }
     531
     532    private void downloadRelationMembers() {
     533        boolean download = false;
     534        for (RelationMember member : clone.members) {
     535            if (member.member.incomplete) {
     536                download = true;
     537                break;
     538            }
     539        }
     540        if (download) {
     541            OsmServerObjectReader reader = new OsmServerObjectReader(clone.id, OsmServerObjectReader.TYPE_REL, true);
     542            try {
     543                DataSet dataSet = reader.parseOsm();
     544                if (dataSet != null) {
     545                    final MergeVisitor visitor = new MergeVisitor(Main.main
     546                            .editLayer().data, dataSet);
     547                    for (final OsmPrimitive osm : dataSet.allPrimitives())
     548                        osm.visit(visitor);
     549                    visitor.fixReferences();
     550
     551                    // copy the merged layer's data source info
     552                    for (DataSource src : dataSet.dataSources)
     553                        Main.main.editLayer().data.dataSources.add(src);
     554                    Main.main.editLayer().fireDataChange();
     555
     556                    if (visitor.conflicts.isEmpty())
     557                        return;
     558                    final ConflictDialog dlg = Main.map.conflictDialog;
     559                    dlg.add(visitor.conflicts);
     560                    JOptionPane.showMessageDialog(Main.parent,
     561                            tr("There were conflicts during import."));
     562                    if (!dlg.isVisible())
     563                        dlg.action
     564                                .actionPerformed(new ActionEvent(this, 0, ""));
     565                }
     566
     567            } catch (SAXException e) {
     568                e.printStackTrace();
     569                JOptionPane.showMessageDialog(this,tr("Error parsing server response.")+": "+e.getMessage(),
     570                tr("Error"), JOptionPane.ERROR_MESSAGE);
     571            } catch (IOException e) {
     572                e.printStackTrace();
     573                JOptionPane.showMessageDialog(this,tr("Cannot connect to server.")+": "+e.getMessage(),
     574                tr("Error"), JOptionPane.ERROR_MESSAGE);
     575            }
     576        }
     577    }
    578578}
  • trunk/src/org/openstreetmap/josm/gui/dialogs/RelationListDialog.java

    r1084 r1169  
    4545public class RelationListDialog extends ToggleDialog implements LayerChangeListener, DataChangeListener {
    4646
    47         /**
    48         * The selection's list data.
    49         */
    50         private final DefaultListModel list = new DefaultListModel();
     47    /**
     48    * The selection's list data.
     49    */
     50    private final DefaultListModel list = new DefaultListModel();
    5151
    52         /**
    53         * The display list.
    54         */
    55         private JList displaylist = new JList(list);
     52    /**
     53    * The display list.
     54    */
     55    private JList displaylist = new JList(list);
    5656
    57         public RelationListDialog() {
    58                 super(tr("Relations"), "relationlist", tr("Open a list of all relations."),
    59                 Shortcut.registerShortcut("subwindow:relations", tr("Toggle: {0}", tr("Relations")), KeyEvent.VK_R, Shortcut.GROUP_LAYER), 150);
    60                 displaylist.setCellRenderer(new OsmPrimitivRenderer());
    61                 displaylist.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
    62                 displaylist.addMouseListener(new MouseAdapter(){
    63                         @Override public void mouseClicked(MouseEvent e) {
    64                                 if (e.getClickCount() < 2)
    65                                         return;
    66                                 Relation toEdit = (Relation) displaylist.getSelectedValue();
    67                                 if (toEdit != null)
    68                                         new RelationEditor(toEdit).setVisible(true);
    69                         }
    70                 });
     57    public RelationListDialog() {
     58        super(tr("Relations"), "relationlist", tr("Open a list of all relations."),
     59        Shortcut.registerShortcut("subwindow:relations", tr("Toggle: {0}", tr("Relations")), KeyEvent.VK_R, Shortcut.GROUP_LAYER), 150);
     60        displaylist.setCellRenderer(new OsmPrimitivRenderer());
     61        displaylist.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
     62        displaylist.addMouseListener(new MouseAdapter(){
     63            @Override public void mouseClicked(MouseEvent e) {
     64                if (e.getClickCount() < 2)
     65                    return;
     66                Relation toEdit = (Relation) displaylist.getSelectedValue();
     67                if (toEdit != null)
     68                    new RelationEditor(toEdit).setVisible(true);
     69            }
     70        });
    7171
    72                 add(new JScrollPane(displaylist), BorderLayout.CENTER);
     72        add(new JScrollPane(displaylist), BorderLayout.CENTER);
    7373
    74                 JPanel buttonPanel = new JPanel(new GridLayout(1,4));
     74        JPanel buttonPanel = new JPanel(new GridLayout(1,4));
    7575
    76                 buttonPanel.add(new SideButton(marktr("New"), "addrelation", "Selection", tr("Create a new relation"), new ActionListener() {
    77                         public void actionPerformed(ActionEvent e) {
    78                                 // call relation editor with null argument to create new relation
    79                                 new RelationEditor(null).setVisible(true);
    80                         }
    81                 }), GBC.std());
     76        buttonPanel.add(new SideButton(marktr("New"), "addrelation", "Selection", tr("Create a new relation"), new ActionListener() {
     77            public void actionPerformed(ActionEvent e) {
     78                // call relation editor with null argument to create new relation
     79                new RelationEditor(null).setVisible(true);
     80            }
     81        }), GBC.std());
    8282
    83                 buttonPanel.add(new SideButton(marktr("Select"), "select", "Selection", tr("Select this relation"), new ActionListener() {
    84                         public void actionPerformed(ActionEvent e) {
    85                                 // replace selection with the relation from the list
    86                                 Main.ds.setSelected((Relation)displaylist.getSelectedValue());
    87                         }
    88                 }), GBC.std());
     83        buttonPanel.add(new SideButton(marktr("Select"), "select", "Selection", tr("Select this relation"), new ActionListener() {
     84            public void actionPerformed(ActionEvent e) {
     85                // replace selection with the relation from the list
     86                Main.ds.setSelected((Relation)displaylist.getSelectedValue());
     87            }
     88        }), GBC.std());
    8989
    90                 buttonPanel.add(new SideButton(marktr("Edit"), "edit", "Selection", tr( "Open an editor for the selected relation"), new ActionListener() {
    91                         public void actionPerformed(ActionEvent e) {
    92                                 Relation toEdit = (Relation) displaylist.getSelectedValue();
    93                                 if (toEdit != null)
    94                                         new RelationEditor(toEdit).setVisible(true);
    95                         }
    96                 }), GBC.std());
     90        buttonPanel.add(new SideButton(marktr("Edit"), "edit", "Selection", tr( "Open an editor for the selected relation"), new ActionListener() {
     91            public void actionPerformed(ActionEvent e) {
     92                Relation toEdit = (Relation) displaylist.getSelectedValue();
     93                if (toEdit != null)
     94                    new RelationEditor(toEdit).setVisible(true);
     95            }
     96        }), GBC.std());
    9797
    98                 buttonPanel.add(new SideButton(marktr("Delete"), "delete", "Selection", tr("Delete the selected relation"), new ActionListener() {
    99                         public void actionPerformed(ActionEvent e) {
    100                                 Relation toDelete = (Relation) displaylist.getSelectedValue();
    101                                 if (toDelete != null) {
    102                                         Main.main.undoRedo.add(
    103                                                 new DeleteCommand(Collections.singleton(toDelete)));
    104                                 }
    105                         }
    106                 }), GBC.eol());
    107                 Layer.listeners.add(this);
    108                 add(buttonPanel, BorderLayout.SOUTH);
    109         }
     98        buttonPanel.add(new SideButton(marktr("Delete"), "delete", "Selection", tr("Delete the selected relation"), new ActionListener() {
     99            public void actionPerformed(ActionEvent e) {
     100                Relation toDelete = (Relation) displaylist.getSelectedValue();
     101                if (toDelete != null) {
     102                    Main.main.undoRedo.add(
     103                        new DeleteCommand(Collections.singleton(toDelete)));
     104                }
     105            }
     106        }), GBC.eol());
     107        Layer.listeners.add(this);
     108        add(buttonPanel, BorderLayout.SOUTH);
     109    }
    110110
    111         @Override public void setVisible(boolean b) {
    112                 super.setVisible(b);
    113                 if (b) updateList();
    114         }
     111    @Override public void setVisible(boolean b) {
     112        super.setVisible(b);
     113        if (b) updateList();
     114    }
    115115
    116         public void updateList() {
    117                 list.setSize(Main.ds.relations.size());
    118                 int i = 0;
    119                 for (OsmPrimitive e : DataSet.sort(Main.ds.relations)) {
    120                         if (!e.deleted && !e.incomplete)
    121                                 list.setElementAt(e, i++);
    122                 }
    123                 list.setSize(i);
    124         }
     116    public void updateList() {
     117        list.setSize(Main.ds.relations.size());
     118        int i = 0;
     119        for (OsmPrimitive e : DataSet.sort(Main.ds.relations)) {
     120            if (!e.deleted && !e.incomplete)
     121                list.setElementAt(e, i++);
     122        }
     123        list.setSize(i);
     124    }
    125125
    126         public void activeLayerChange(Layer a, Layer b) {
    127                 if ((a == null || a instanceof OsmDataLayer) && b instanceof OsmDataLayer) {
    128                         if (a != null) ((OsmDataLayer)a).listenerDataChanged.remove(this);
    129                         ((OsmDataLayer)b).listenerDataChanged.add(this);
    130                         updateList();
    131                         repaint();
    132                 }
    133         }
     126    public void activeLayerChange(Layer a, Layer b) {
     127        if ((a == null || a instanceof OsmDataLayer) && b instanceof OsmDataLayer) {
     128            if (a != null) ((OsmDataLayer)a).listenerDataChanged.remove(this);
     129            ((OsmDataLayer)b).listenerDataChanged.add(this);
     130            updateList();
     131            repaint();
     132        }
     133    }
    134134
    135         public void layerRemoved(Layer a) {
    136                 if (a instanceof OsmDataLayer) {
    137                         ((OsmDataLayer)a).listenerDataChanged.remove(this);
    138                 }
    139         }
    140         public void layerAdded(Layer a) {
    141                 if (a instanceof OsmDataLayer) {
    142                         ((OsmDataLayer)a).listenerDataChanged.add(this);
    143                 }
    144         }
    145         public void dataChanged(OsmDataLayer l) {
    146                 updateList();
    147                 repaint();
    148         }
     135    public void layerRemoved(Layer a) {
     136        if (a instanceof OsmDataLayer) {
     137            ((OsmDataLayer)a).listenerDataChanged.remove(this);
     138        }
     139    }
     140    public void layerAdded(Layer a) {
     141        if (a instanceof OsmDataLayer) {
     142            ((OsmDataLayer)a).listenerDataChanged.add(this);
     143        }
     144    }
     145    public void dataChanged(OsmDataLayer l) {
     146        updateList();
     147        repaint();
     148    }
    149149
    150         /**
    151         * Returns the currently selected relation, or null.
    152         *
    153         * @return the currently selected relation, or null
    154         */
    155         public Relation getCurrentRelation() {
    156                 return (Relation) displaylist.getSelectedValue();
    157         }
     150    /**
     151    * Returns the currently selected relation, or null.
     152    *
     153    * @return the currently selected relation, or null
     154    */
     155    public Relation getCurrentRelation() {
     156        return (Relation) displaylist.getSelectedValue();
     157    }
    158158
    159         /**
    160         * Adds a selection listener to the relation list.
    161         *
    162         * @param listener the listener to add
    163         */
    164         public void addListSelectionListener(ListSelectionListener listener) {
    165                 displaylist.addListSelectionListener(listener);
    166         }
     159    /**
     160    * Adds a selection listener to the relation list.
     161    *
     162    * @param listener the listener to add
     163    */
     164    public void addListSelectionListener(ListSelectionListener listener) {
     165        displaylist.addListSelectionListener(listener);
     166    }
    167167
    168         /**
    169         * Removes a selection listener from the relation list.
    170         *
    171         * @param listener the listener to remove
    172         */
    173         public void removeListSelectionListener(ListSelectionListener listener) {
    174                 displaylist.removeListSelectionListener(listener);
    175         }
     168    /**
     169    * Removes a selection listener from the relation list.
     170    *
     171    * @param listener the listener to remove
     172    */
     173    public void removeListSelectionListener(ListSelectionListener listener) {
     174        displaylist.removeListSelectionListener(listener);
     175    }
    176176}
  • trunk/src/org/openstreetmap/josm/gui/dialogs/ToggleDialog.java

    r1084 r1169  
    3636public class ToggleDialog extends JPanel implements Helpful {
    3737
    38         public final class ToggleDialogAction extends JosmAction {
    39                 public final String prefname;
    40                 public AbstractButton button;
     38    public final class ToggleDialogAction extends JosmAction {
     39        public final String prefname;
     40        public AbstractButton button;
    4141
    42                 private ToggleDialogAction(String name, String iconName, String tooltip, Shortcut shortcut, String prefname) {
    43                         super(name, iconName, tooltip, shortcut, false);
    44                         this.prefname = prefname;
    45                 }
     42        private ToggleDialogAction(String name, String iconName, String tooltip, Shortcut shortcut, String prefname) {
     43            super(name, iconName, tooltip, shortcut, false);
     44            this.prefname = prefname;
     45        }
    4646
    47                 public void actionPerformed(ActionEvent e) {
    48                         if (e != null && !(e.getSource() instanceof AbstractButton))
    49                                 button.setSelected(!button.isSelected());
    50                         setVisible(button.isSelected());
    51                         Main.pref.put(prefname+".visible", button.isSelected());
    52                 }
    53         }
     47        public void actionPerformed(ActionEvent e) {
     48            if (e != null && !(e.getSource() instanceof AbstractButton))
     49                button.setSelected(!button.isSelected());
     50            setVisible(button.isSelected());
     51            Main.pref.put(prefname+".visible", button.isSelected());
     52        }
     53    }
    5454
    55         /**
    56         * The action to toggle this dialog.
    57         */
    58         public ToggleDialogAction action;
    59         public final String prefName;
     55    /**
     56    * The action to toggle this dialog.
     57    */
     58    public ToggleDialogAction action;
     59    public final String prefName;
    6060
    61         public JPanel parent;
    62         private final JPanel titleBar = new JPanel(new GridBagLayout());
     61    public JPanel parent;
     62    private final JPanel titleBar = new JPanel(new GridBagLayout());
    6363
    64         @Deprecated
    65         public ToggleDialog(final String name, String iconName, String tooltip, int shortcut, int preferredHeight) {
    66                 super(new BorderLayout());
    67                 this.prefName = iconName;
    68                 ToggleDialogInit(name, iconName, tooltip, Shortcut.registerShortcut("auto:"+name, tooltip, shortcut, Shortcut.GROUP_LAYER), preferredHeight);
    69         }
     64    @Deprecated
     65    public ToggleDialog(final String name, String iconName, String tooltip, int shortcut, int preferredHeight) {
     66        super(new BorderLayout());
     67        this.prefName = iconName;
     68        ToggleDialogInit(name, iconName, tooltip, Shortcut.registerShortcut("auto:"+name, tooltip, shortcut, Shortcut.GROUP_LAYER), preferredHeight);
     69    }
    7070
    71         public ToggleDialog(final String name, String iconName, String tooltip, Shortcut shortcut, int preferredHeight) {
    72                 super(new BorderLayout());
    73                 this.prefName = iconName;
    74                 ToggleDialogInit(name, iconName, tooltip, shortcut, preferredHeight);
    75         }
     71    public ToggleDialog(final String name, String iconName, String tooltip, Shortcut shortcut, int preferredHeight) {
     72        super(new BorderLayout());
     73        this.prefName = iconName;
     74        ToggleDialogInit(name, iconName, tooltip, shortcut, preferredHeight);
     75    }
    7676
    77         private void ToggleDialogInit(final String name, String iconName, String tooltip, Shortcut shortcut, int preferredHeight) {
    78                 setPreferredSize(new Dimension(330,preferredHeight));
    79                 action = new ToggleDialogAction(name, "dialogs/"+iconName, tooltip, shortcut, iconName);
    80                 String helpId = "Dialog/"+getClass().getName().substring(getClass().getName().lastIndexOf('.')+1);
    81                 action.putValue("help", helpId.substring(0, helpId.length()-6));
    82                 setLayout(new BorderLayout());
     77    private void ToggleDialogInit(final String name, String iconName, String tooltip, Shortcut shortcut, int preferredHeight) {
     78        setPreferredSize(new Dimension(330,preferredHeight));
     79        action = new ToggleDialogAction(name, "dialogs/"+iconName, tooltip, shortcut, iconName);
     80        String helpId = "Dialog/"+getClass().getName().substring(getClass().getName().lastIndexOf('.')+1);
     81        action.putValue("help", helpId.substring(0, helpId.length()-6));
     82        setLayout(new BorderLayout());
    8383
    84                 titleBar.add(new JLabel(name), GBC.std());
    85                 titleBar.add(Box.createHorizontalGlue(),GBC.std().fill(GBC.HORIZONTAL));
     84        titleBar.add(new JLabel(name), GBC.std());
     85        titleBar.add(Box.createHorizontalGlue(),GBC.std().fill(GBC.HORIZONTAL));
    8686
    87                 JButton sticky = new JButton(ImageProvider.get("misc", "sticky"));
    88                 sticky.setBorder(BorderFactory.createEmptyBorder());
    89                 final ActionListener stickyActionListener = new ActionListener(){
    90                         public void actionPerformed(ActionEvent e) {
    91                                 final JFrame f = new JFrame(name);
    92                                 try {f.setAlwaysOnTop(true);} catch (SecurityException e1) {}
    93                                 parent.remove(ToggleDialog.this);
    94                                 f.getContentPane().add(ToggleDialog.this);
    95                                 f.addWindowListener(new WindowAdapter(){
    96                                         @Override public void windowClosing(WindowEvent e) {
    97                                                 titleBar.setVisible(true);
    98                                                 f.getContentPane().removeAll();
    99                                                 parent.add(ToggleDialog.this);
    100                                                 f.dispose();
     87        JButton sticky = new JButton(ImageProvider.get("misc", "sticky"));
     88        sticky.setBorder(BorderFactory.createEmptyBorder());
     89        final ActionListener stickyActionListener = new ActionListener(){
     90            public void actionPerformed(ActionEvent e) {
     91                final JFrame f = new JFrame(name);
     92                try {f.setAlwaysOnTop(true);} catch (SecurityException e1) {}
     93                parent.remove(ToggleDialog.this);
     94                f.getContentPane().add(ToggleDialog.this);
     95                f.addWindowListener(new WindowAdapter(){
     96                    @Override public void windowClosing(WindowEvent e) {
     97                        titleBar.setVisible(true);
     98                        f.getContentPane().removeAll();
     99                        parent.add(ToggleDialog.this);
     100                        f.dispose();
    101101
    102                                                 // doLayout() - workaround
    103                                                 setVisible(false);
    104                                                 setVisible(true);
     102                        // doLayout() - workaround
     103                        setVisible(false);
     104                        setVisible(true);
    105105
    106                                                 Main.pref.put(action.prefname+".docked", true);
    107                                         }
    108                                 });
    109                                 f.addComponentListener(new ComponentAdapter(){
    110                                         @Override public void componentMoved(ComponentEvent e) {
    111                                                 Main.pref.put(action.prefname+".bounds", f.getX()+","+f.getY()+","+f.getWidth()+","+f.getHeight());
     106                        Main.pref.put(action.prefname+".docked", true);
    112107                    }
    113                                 });
    114                                 String bounds = Main.pref.get(action.prefname+".bounds",null);
    115                                 if (bounds != null) {
    116                                         String[] b = bounds.split(",");
    117                                         f.setBounds(Integer.parseInt(b[0]),Integer.parseInt(b[1]),Integer.parseInt(b[2]),Integer.parseInt(b[3]));
    118                                 } else
    119                                         f.pack();
    120                                 Main.pref.put(action.prefname+".docked", false);
    121                                 f.setVisible(true);
    122                                 titleBar.setVisible(false);
     108                });
     109                f.addComponentListener(new ComponentAdapter(){
     110                    @Override public void componentMoved(ComponentEvent e) {
     111                        Main.pref.put(action.prefname+".bounds", f.getX()+","+f.getY()+","+f.getWidth()+","+f.getHeight());
     112                    }
     113                });
     114                String bounds = Main.pref.get(action.prefname+".bounds",null);
     115                if (bounds != null) {
     116                    String[] b = bounds.split(",");
     117                    f.setBounds(Integer.parseInt(b[0]),Integer.parseInt(b[1]),Integer.parseInt(b[2]),Integer.parseInt(b[3]));
     118                } else
     119                    f.pack();
     120                Main.pref.put(action.prefname+".docked", false);
     121                f.setVisible(true);
     122                titleBar.setVisible(false);
    123123
    124                                 // doLayout() - workaround
    125                                 parent.setVisible(false);
    126                                 parent.setVisible(true);
    127                         }
    128                 };
    129                 sticky.addActionListener(stickyActionListener);
     124                // doLayout() - workaround
     125                parent.setVisible(false);
     126                parent.setVisible(true);
     127            }
     128        };
     129        sticky.addActionListener(stickyActionListener);
    130130
    131                 titleBar.add(sticky);
    132                 add(titleBar, BorderLayout.NORTH);
     131        titleBar.add(sticky);
     132        add(titleBar, BorderLayout.NORTH);
    133133
    134                 setVisible(false);
    135                 setBorder(BorderFactory.createEtchedBorder());
     134        setVisible(false);
     135        setBorder(BorderFactory.createEtchedBorder());
    136136
    137                 if (!Main.pref.getBoolean(action.prefname+".docked", true)) {
    138                         EventQueue.invokeLater(new Runnable(){
    139                                 public void run() {
    140                                         stickyActionListener.actionPerformed(null);
     137        if (!Main.pref.getBoolean(action.prefname+".docked", true)) {
     138            EventQueue.invokeLater(new Runnable(){
     139                public void run() {
     140                    stickyActionListener.actionPerformed(null);
    141141                }
    142                         });
    143                 }
    144         }
     142            });
     143        }
     144    }
    145145
    146         public String helpTopic() {
    147                 String help = getClass().getName();
    148                 help = help.substring(help.lastIndexOf('.')+1, help.length()-6);
    149                 return "Dialog/"+help;
    150         }
     146    public String helpTopic() {
     147        String help = getClass().getName();
     148        help = help.substring(help.lastIndexOf('.')+1, help.length()-6);
     149        return "Dialog/"+help;
     150    }
    151151}
  • trunk/src/org/openstreetmap/josm/gui/dialogs/UserListDialog.java

    r1084 r1169  
    3434public class UserListDialog extends ToggleDialog implements SelectionChangedListener, MouseListener{
    3535
    36         /**
    37         * The display list.
    38         */
    39         private final DefaultTableModel data = new DefaultTableModel() {
    40                 @Override public boolean isCellEditable(int row, int column) {
    41                         return false;
    42                 }
    43                 @Override public Class<?> getColumnClass(int columnIndex) {
    44                         return columnIndex == 0 ? String.class : Integer.class;
    45                 }
    46         };
     36    /**
     37    * The display list.
     38    */
     39    private final DefaultTableModel data = new DefaultTableModel() {
     40        @Override public boolean isCellEditable(int row, int column) {
     41            return false;
     42        }
     43        @Override public Class<?> getColumnClass(int columnIndex) {
     44            return columnIndex == 0 ? String.class : Integer.class;
     45        }
     46    };
    4747
    48         private JTable userTable = new JTable(data);
     48    private JTable userTable = new JTable(data);
    4949
    5050    private static User anonymousUser = User.get("(anonymous users)");
    5151
    52         public UserListDialog() {
    53                 super(tr("Authors"), "userlist", tr("Open a list of people working on the selected objects."),
    54                 Shortcut.registerShortcut("subwindow:authors", tr("Toggle: {0}", tr("Authors")), KeyEvent.VK_A, Shortcut.GROUP_LAYER, Shortcut.SHIFT_DEFAULT), 150);
     52    public UserListDialog() {
     53        super(tr("Authors"), "userlist", tr("Open a list of people working on the selected objects."),
     54        Shortcut.registerShortcut("subwindow:authors", tr("Toggle: {0}", tr("Authors")), KeyEvent.VK_A, Shortcut.GROUP_LAYER, Shortcut.SHIFT_DEFAULT), 150);
    5555
    56                 data.setColumnIdentifiers(new String[]{tr("Author"),tr("# Objects"),"%"});
    57                 userTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
    58                 add(new JScrollPane(userTable), BorderLayout.CENTER);
    59                 selectionChanged(Main.ds.getSelected());
    60                 userTable.addMouseListener(this);
    61                 DataSet.selListeners.add(this);
    62         }
     56        data.setColumnIdentifiers(new String[]{tr("Author"),tr("# Objects"),"%"});
     57        userTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
     58        add(new JScrollPane(userTable), BorderLayout.CENTER);
     59        selectionChanged(Main.ds.getSelected());
     60        userTable.addMouseListener(this);
     61        DataSet.selListeners.add(this);
     62    }
    6363
    64         @Override public void setVisible(boolean b) {
    65                 super.setVisible(b);
    66                 if (b)
    67                         selectionChanged(Main.ds.getSelected());
    68         }
     64    @Override public void setVisible(boolean b) {
     65        super.setVisible(b);
     66        if (b)
     67            selectionChanged(Main.ds.getSelected());
     68    }
    6969
    70         /**
    71         * Called when the selection in the dataset changed.
    72         * @param newSelection The new selection array.
    73         */
    74         public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) {
    75                 if (!isVisible())
    76                         return;
     70    /**
     71    * Called when the selection in the dataset changed.
     72    * @param newSelection The new selection array.
     73    */
     74    public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) {
     75        if (!isVisible())
     76            return;
    7777
    78                 class UserCount {
    79                         User user;
    80                         int count;
    81                         UserCount(User user, int count) { this.user=user; this.count=count; }
    82                 }
     78        class UserCount {
     79            User user;
     80            int count;
     81            UserCount(User user, int count) { this.user=user; this.count=count; }
     82        }
    8383
    84                 if (data == null)
    85                         return; // selection changed may be received in base class constructor before init
     84        if (data == null)
     85            return; // selection changed may be received in base class constructor before init
    8686
    87                 data.setRowCount(0);
     87        data.setRowCount(0);
    8888
    89                 HashMap<User,UserCount> counters = new HashMap<User,UserCount>();
    90                 int all = 0;
    91                 for (OsmPrimitive p : newSelection) {
     89        HashMap<User,UserCount> counters = new HashMap<User,UserCount>();
     90        int all = 0;
     91        for (OsmPrimitive p : newSelection) {
    9292            User u = p.user;
    9393            if (u == null) u = anonymousUser;
     
    9797            uc.count++;
    9898            all++;
    99                 }
    100                 UserCount[] ucArr = new UserCount[counters.size()];
    101                 counters.values().toArray(ucArr);
    102                 Arrays.sort(ucArr, new Comparator<UserCount>() {
    103                         public int compare(UserCount a, UserCount b) {
    104                                 return (a.count<b.count) ? 1 : (a.count>b.count) ? -1 : 0;
    105                         }
    106                 });
     99        }
     100        UserCount[] ucArr = new UserCount[counters.size()];
     101        counters.values().toArray(ucArr);
     102        Arrays.sort(ucArr, new Comparator<UserCount>() {
     103            public int compare(UserCount a, UserCount b) {
     104                return (a.count<b.count) ? 1 : (a.count>b.count) ? -1 : 0;
     105            }
     106        });
    107107
    108                 for (UserCount uc : ucArr) {
    109                         data.addRow(new Object[] { uc.user.name, uc.count, uc.count * 100 / all });
    110                 }
    111         }
     108        for (UserCount uc : ucArr) {
     109            data.addRow(new Object[] { uc.user.name, uc.count, uc.count * 100 / all });
     110        }
     111    }
    112112
    113113    public void mouseClicked(MouseEvent e) {
     
    115115            int index = userTable.getSelectedRow();
    116116            String userName = (String) data.getValueAt(index, 0);
    117             if (userName==null) 
     117            if (userName==null)
    118118                return;
    119119            Collection<OsmPrimitive> selected = Main.ds.getSelected();
  • trunk/src/org/openstreetmap/josm/gui/download/BookmarkSelection.java

    r733 r1169  
    2727/**
    2828 * Bookmark selector.
    29  * 
     29 *
    3030 * Provides selection, creation and deletion of bookmarks.
    3131 * Extracted from old DownloadAction.
    32  * 
     32 *
    3333 * @author Frederik Ramm <frederik@remote.org>
    3434 *
     
    3636public class BookmarkSelection implements DownloadSelection {
    3737
    38         private Preferences.Bookmark tempBookmark = null;
    39         private BookmarkList bookmarks;
    40        
    41         public void addGui(final DownloadDialog gui) {
    42                
    43                 JPanel dlg = new JPanel(new GridBagLayout());
    44                 gui.tabpane.addTab(tr("Bookmarks"), dlg);
     38    private Preferences.Bookmark tempBookmark = null;
     39    private BookmarkList bookmarks;
    4540
    46                 bookmarks = new BookmarkList();
    47                
    48                 /* add a handler for "double click" mouse events */
    49                 MouseListener mouseListener = new MouseAdapter() {
    50                         @Override public void mouseClicked(MouseEvent e) {
    51                                 if (e.getClickCount() == 2) {
    52                                         //int index = bookmarks.locationToIndex(e.getPoint());
    53                                         gui.closeDownloadDialog(true);
    54                                 }
    55                         }
    56                 };
    57                 bookmarks.addMouseListener(mouseListener);
    58                
    59                 bookmarks.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
    60                         public void valueChanged(ListSelectionEvent e) {
    61                                 Preferences.Bookmark b = (Preferences.Bookmark)bookmarks.getSelectedValue();
    62                                 if (b != null) {
    63                                         gui.minlat = b.latlon[0];
    64                                         gui.minlon = b.latlon[1];
    65                                         gui.maxlat = b.latlon[2];
    66                                         gui.maxlon = b.latlon[3];
    67                                         gui.boundingBoxChanged(BookmarkSelection.this);
    68                                 }
    69                         }
    70                 });
    71                 //wc.addListMarker(bookmarks);
    72                 dlg.add(new JScrollPane(bookmarks), GBC.eol().fill());
     41    public void addGui(final DownloadDialog gui) {
    7342
    74                 JPanel buttons = new JPanel(new GridLayout(1,2));
    75                 JButton add = new JButton(tr("Add"));
    76                 add.addActionListener(new ActionListener(){
    77                         public void actionPerformed(ActionEvent e) {
    78                                
    79                                 if (tempBookmark == null) {
    80                                         JOptionPane.showMessageDialog(Main.parent, tr("Please enter the desired coordinates first."));
    81                                         return;
    82                                 }
    83                                 tempBookmark.name = JOptionPane.showInputDialog(Main.parent,tr("Please enter a name for the location."));
    84                                 if (tempBookmark.name != null && !tempBookmark.name.equals("")) {
    85                                         ((DefaultListModel)bookmarks.getModel()).addElement(tempBookmark);
    86                                         bookmarks.save();
    87                                 }
    88                         }
    89                 });
    90                 buttons.add(add);
    91                 JButton remove = new JButton(tr("Remove"));
    92                 remove.addActionListener(new ActionListener(){
    93                         public void actionPerformed(ActionEvent e) {
    94                                 Object sel = bookmarks.getSelectedValue();
    95                                 if (sel == null) {
    96                                         JOptionPane.showMessageDialog(Main.parent,tr("Select a bookmark first."));
    97                                         return;
    98                                 }
    99                                 ((DefaultListModel)bookmarks.getModel()).removeElement(sel);
    100                                 bookmarks.save();
    101                         }
    102                 });
    103                 buttons.add(remove);
    104                 dlg.add(buttons, GBC.eop().fill(GBC.HORIZONTAL));
    105         }               
     43        JPanel dlg = new JPanel(new GridBagLayout());
     44        gui.tabpane.addTab(tr("Bookmarks"), dlg);
    10645
    107         public void boundingBoxChanged(DownloadDialog gui) {
    108                 tempBookmark = new Preferences.Bookmark();
    109                 tempBookmark.latlon[0] = gui.minlat;
    110                 tempBookmark.latlon[1] = gui.minlon;
    111                 tempBookmark.latlon[2] = gui.maxlat;
    112                 tempBookmark.latlon[3] = gui.maxlon;
    113                 bookmarks.clearSelection();
    114         }
     46        bookmarks = new BookmarkList();
     47
     48        /* add a handler for "double click" mouse events */
     49        MouseListener mouseListener = new MouseAdapter() {
     50            @Override public void mouseClicked(MouseEvent e) {
     51                if (e.getClickCount() == 2) {
     52                    //int index = bookmarks.locationToIndex(e.getPoint());
     53                    gui.closeDownloadDialog(true);
     54                }
     55            }
     56        };
     57        bookmarks.addMouseListener(mouseListener);
     58
     59        bookmarks.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
     60            public void valueChanged(ListSelectionEvent e) {
     61                Preferences.Bookmark b = (Preferences.Bookmark)bookmarks.getSelectedValue();
     62                if (b != null) {
     63                    gui.minlat = b.latlon[0];
     64                    gui.minlon = b.latlon[1];
     65                    gui.maxlat = b.latlon[2];
     66                    gui.maxlon = b.latlon[3];
     67                    gui.boundingBoxChanged(BookmarkSelection.this);
     68                }
     69            }
     70        });
     71        //wc.addListMarker(bookmarks);
     72        dlg.add(new JScrollPane(bookmarks), GBC.eol().fill());
     73
     74        JPanel buttons = new JPanel(new GridLayout(1,2));
     75        JButton add = new JButton(tr("Add"));
     76        add.addActionListener(new ActionListener(){
     77            public void actionPerformed(ActionEvent e) {
     78
     79                if (tempBookmark == null) {
     80                    JOptionPane.showMessageDialog(Main.parent, tr("Please enter the desired coordinates first."));
     81                    return;
     82                }
     83                tempBookmark.name = JOptionPane.showInputDialog(Main.parent,tr("Please enter a name for the location."));
     84                if (tempBookmark.name != null && !tempBookmark.name.equals("")) {
     85                    ((DefaultListModel)bookmarks.getModel()).addElement(tempBookmark);
     86                    bookmarks.save();
     87                }
     88            }
     89        });
     90        buttons.add(add);
     91        JButton remove = new JButton(tr("Remove"));
     92        remove.addActionListener(new ActionListener(){
     93            public void actionPerformed(ActionEvent e) {
     94                Object sel = bookmarks.getSelectedValue();
     95                if (sel == null) {
     96                    JOptionPane.showMessageDialog(Main.parent,tr("Select a bookmark first."));
     97                    return;
     98                }
     99                ((DefaultListModel)bookmarks.getModel()).removeElement(sel);
     100                bookmarks.save();
     101            }
     102        });
     103        buttons.add(remove);
     104        dlg.add(buttons, GBC.eop().fill(GBC.HORIZONTAL));
     105    }
     106
     107    public void boundingBoxChanged(DownloadDialog gui) {
     108        tempBookmark = new Preferences.Bookmark();
     109        tempBookmark.latlon[0] = gui.minlat;
     110        tempBookmark.latlon[1] = gui.minlon;
     111        tempBookmark.latlon[2] = gui.maxlat;
     112        tempBookmark.latlon[3] = gui.maxlon;
     113        bookmarks.clearSelection();
     114    }
    115115
    116116
  • trunk/src/org/openstreetmap/josm/gui/download/BoundingBoxSelection.java

    r1064 r1169  
    2626/**
    2727 * Bounding box selector.
    28  * 
     28 *
    2929 * Provides max/min lat/lon input fields as well as the "URL from www.openstreetmap.org" text field.
    30  * 
     30 *
    3131 * @author Frederik Ramm <frederik@remote.org>
    3232 *
     
    3434public class BoundingBoxSelection implements DownloadSelection {
    3535
    36         private JTextField[] latlon = new JTextField[] {
    37                         new JTextField(11),
    38                         new JTextField(11),
    39                         new JTextField(11),
    40                         new JTextField(11) };
    41         final JTextArea osmUrl = new JTextArea();
    42         final JTextArea showUrl = new JTextArea();
    43         String noteUrl = tr("You can paste an URL here to download the area.");
    44        
    45         public void addGui(final DownloadDialog gui) {
    46 
    47                 JPanel dlg = new JPanel(new GridBagLayout());
    48                 osmUrl.setText(noteUrl);
    49 
    50                 final FocusListener dialogUpdater = new FocusAdapter() {
    51                         @Override public void focusLost(FocusEvent e) {
    52                                 SwingUtilities.invokeLater(new Runnable() {
    53                                         public void run() {
    54                                                 try {
    55                                                         double minlat = Double.parseDouble(latlon[0].getText());
    56                                                         double minlon = Double.parseDouble(latlon[1].getText());
    57                                                         double maxlat = Double.parseDouble(latlon[2].getText());
    58                                                         double maxlon = Double.parseDouble(latlon[3].getText());
    59                                                         if (minlat != gui.minlat || minlon != gui.minlon || maxlat != gui.maxlat || maxlon != gui.maxlon) {
    60                                                                 gui.minlat = minlat; gui.minlon = minlon;
    61                                                                 gui.maxlat = maxlat; gui.maxlon = maxlon;
    62                                                                 gui.boundingBoxChanged(BoundingBoxSelection.this);
    63                                                         }
    64                                                 } catch (NumberFormatException x) {
    65                                                         // ignore
    66                                                 }
    67                                                 updateUrl(gui);
    68                                         }
    69                                 });
    70                         }
    71                 };
    72                
    73                 for (JTextField f : latlon) {
    74                         f.setMinimumSize(new Dimension(100,new JTextField().getMinimumSize().height));
    75                         f.addFocusListener(dialogUpdater);
    76                 }
    77                 class osmUrlRefresher implements DocumentListener {
    78                         public void changedUpdate(DocumentEvent e) { dowork(); }
    79                         public void insertUpdate(DocumentEvent e) { dowork(); }
    80                         public void removeUpdate(DocumentEvent e) { dowork(); }
    81                         private void dowork() {
    82                                 Bounds b = osmurl2bounds(osmUrl.getText());
    83                                 if (b != null) {
    84                                         gui.minlon = b.min.lon();
    85                                         gui.minlat = b.min.lat();
    86                                         gui.maxlon = b.max.lon();
    87                                         gui.maxlat = b.max.lat();
    88                                         gui.boundingBoxChanged(BoundingBoxSelection.this);
    89                                         updateBboxFields(gui);
    90                                         updateUrl(gui);
    91                                 }
    92                         }
    93                 }
    94                
    95                 osmUrl.getDocument().addDocumentListener(new osmUrlRefresher());
    96                
    97                 // select content on receiving focus. this seems to be the default in the
    98                 // windows look+feel but not for others. needs invokeLater to avoid strange
    99                 // side effects that will cancel out the newly made selection otherwise.
    100                 osmUrl.addFocusListener(new FocusAdapter() {
    101                         @Override public void focusGained(FocusEvent e) {
    102                         SwingUtilities.invokeLater(new Runnable() {
    103                                 public void run() {
    104                                         osmUrl.selectAll();
    105                                         }
    106                                 });
    107                         }
    108                 });
    109                 osmUrl.setLineWrap(true);
    110                 osmUrl.setBorder(latlon[0].getBorder());
    111                
    112                 dlg.add(new JLabel(tr("min lat")), GBC.std().insets(10,20,5,0));
    113                 dlg.add(latlon[0], GBC.std().insets(0,20,0,0));
    114                 dlg.add(new JLabel(tr("min lon")), GBC.std().insets(10,20,5,0));
    115                 dlg.add(latlon[1], GBC.eol().insets(0,20,0,0));
    116                 dlg.add(new JLabel(tr("max lat")), GBC.std().insets(10,0,5,0));
    117                 dlg.add(latlon[2], GBC.std());
    118                 dlg.add(new JLabel(tr("max lon")), GBC.std().insets(10,0,5,0));
    119                 dlg.add(latlon[3], GBC.eol());
    120                
    121                 dlg.add(new JLabel(tr("URL from www.openstreetmap.org")), GBC.eol().insets(10,20,5,0));
    122                 dlg.add(osmUrl, GBC.eop().insets(10,0,5,0).fill());
    123                 dlg.add(showUrl, GBC.eop().insets(10,0,5,5));
    124                 showUrl.setEditable(false);
    125                 showUrl.setBackground(dlg.getBackground());
    126                 showUrl.addFocusListener(new FocusAdapter(){
    127                    @Override
    128                     public void focusGained(FocusEvent e) {
    129                         showUrl.selectAll();
    130                     }
    131                 });
    132 
    133                 gui.tabpane.addTab(tr("Bounding Box"), dlg);
    134         }
    135        
    136         /**
    137         * Called when bounding box is changed by one of the other download dialog tabs.
    138         */
    139         public void boundingBoxChanged(DownloadDialog gui) {
    140                 updateBboxFields(gui);
    141                 updateUrl(gui);
    142         }
    143        
    144         private void updateBboxFields(DownloadDialog gui) {
    145                 latlon[0].setText(Double.toString(gui.minlat));
    146                 latlon[1].setText(Double.toString(gui.minlon));
    147                 latlon[2].setText(Double.toString(gui.maxlat));
    148                 latlon[3].setText(Double.toString(gui.maxlon));
    149                 for (JTextField f : latlon)
    150                         f.setCaretPosition(0);
    151         }
    152        
    153         private void updateUrl(DownloadDialog gui) {   
    154                 double lat = (gui.minlat + gui.maxlat)/2;
    155                 double lon = (gui.minlon + gui.maxlon)/2;
    156                 // convert to mercator (for calculation of zoom only)
    157                 double latMin = Math.log(Math.tan(Math.PI/4.0+gui.minlat/180.0*Math.PI/2.0))*180.0/Math.PI;
    158                 double latMax = Math.log(Math.tan(Math.PI/4.0+gui.maxlat/180.0*Math.PI/2.0))*180.0/Math.PI;
    159                 double size = Math.max(Math.abs(latMax-latMin), Math.abs(gui.maxlon-gui.minlon));
    160                 int zoom = 0;
    161                 while (zoom <= 20) {
    162                         if (size >= 180)
    163                                 break;
    164                         size *= 2;
    165                         zoom++;
    166                 }
    167                 showUrl.setText("http://www.openstreetmap.org/index.html?mlat="+lat+"&mlon="+lon+"&zoom="+zoom);
    168         }
    169        
    170         public static Bounds osmurl2bounds(String url) {
    171                 int i = url.indexOf('?');
    172                 if (i == -1)
    173                         return null;
    174                 String[] args = url.substring(i+1).split("&");
    175                 HashMap<String, String> map = new HashMap<String, String>();
    176                 for (String arg : args) {
    177                         int eq = arg.indexOf('=');
    178                         if (eq != -1) {
    179                                 map.put(arg.substring(0, eq), arg.substring(eq + 1));
    180                         }
    181                 }
    182 
    183                 Bounds b = null;
    184                 try {
    185                         if (map.containsKey("bbox")) {
    186                                 String bbox[] = map.get("bbox").split(",");
    187                                 b = new Bounds(
    188                                         new LatLon(Double.parseDouble(bbox[1]), Double.parseDouble(bbox[0])),
    189                                         new LatLon(Double.parseDouble(bbox[3]), Double.parseDouble(bbox[2])));
    190                        
    191                         } else {
    192                                 double size = 180.0 / Math.pow(2, Integer.parseInt(map.get("zoom")));
    193                                 b = new Bounds(
    194                         new LatLon(parseDouble(map, "lat") - size/2, parseDouble(map, "lon") - size),
    195                         new LatLon(parseDouble(map, "lat") + size/2, parseDouble(map, "lon") + size));
    196                         }
    197                 } catch (NumberFormatException x) {
    198                 } catch (NullPointerException x) {
    199                 }
    200                 return b;
    201         }
    202 
    203         private static double parseDouble(HashMap<String, String> map, String key) {
    204                 if (map.containsKey(key))
    205                         return Double.parseDouble(map.get(key));
    206                 return Double.parseDouble(map.get("m"+key));
     36    private JTextField[] latlon = new JTextField[] {
     37            new JTextField(11),
     38            new JTextField(11),
     39            new JTextField(11),
     40            new JTextField(11) };
     41    final JTextArea osmUrl = new JTextArea();
     42    final JTextArea showUrl = new JTextArea();
     43    String noteUrl = tr("You can paste an URL here to download the area.");
     44
     45    public void addGui(final DownloadDialog gui) {
     46
     47        JPanel dlg = new JPanel(new GridBagLayout());
     48        osmUrl.setText(noteUrl);
     49
     50        final FocusListener dialogUpdater = new FocusAdapter() {
     51            @Override public void focusLost(FocusEvent e) {
     52                SwingUtilities.invokeLater(new Runnable() {
     53                    public void run() {
     54                        try {
     55                            double minlat = Double.parseDouble(latlon[0].getText());
     56                            double minlon = Double.parseDouble(latlon[1].getText());
     57                            double maxlat = Double.parseDouble(latlon[2].getText());
     58                            double maxlon = Double.parseDouble(latlon[3].getText());
     59                            if (minlat != gui.minlat || minlon != gui.minlon || maxlat != gui.maxlat || maxlon != gui.maxlon) {
     60                                gui.minlat = minlat; gui.minlon = minlon;
     61                                gui.maxlat = maxlat; gui.maxlon = maxlon;
     62                                gui.boundingBoxChanged(BoundingBoxSelection.this);
     63                            }
     64                        } catch (NumberFormatException x) {
     65                            // ignore
     66                        }
     67                        updateUrl(gui);
     68                    }
     69                });
     70            }
     71        };
     72
     73        for (JTextField f : latlon) {
     74            f.setMinimumSize(new Dimension(100,new JTextField().getMinimumSize().height));
     75            f.addFocusListener(dialogUpdater);
     76        }
     77        class osmUrlRefresher implements DocumentListener {
     78            public void changedUpdate(DocumentEvent e) { dowork(); }
     79            public void insertUpdate(DocumentEvent e) { dowork(); }
     80            public void removeUpdate(DocumentEvent e) { dowork(); }
     81            private void dowork() {
     82                Bounds b = osmurl2bounds(osmUrl.getText());
     83                if (b != null) {
     84                    gui.minlon = b.min.lon();
     85                    gui.minlat = b.min.lat();
     86                    gui.maxlon = b.max.lon();
     87                    gui.maxlat = b.max.lat();
     88                    gui.boundingBoxChanged(BoundingBoxSelection.this);
     89                    updateBboxFields(gui);
     90                    updateUrl(gui);
     91                }
     92            }
     93        }
     94
     95        osmUrl.getDocument().addDocumentListener(new osmUrlRefresher());
     96
     97        // select content on receiving focus. this seems to be the default in the
     98        // windows look+feel but not for others. needs invokeLater to avoid strange
     99        // side effects that will cancel out the newly made selection otherwise.
     100        osmUrl.addFocusListener(new FocusAdapter() {
     101            @Override public void focusGained(FocusEvent e) {
     102                SwingUtilities.invokeLater(new Runnable() {
     103                        public void run() {
     104                            osmUrl.selectAll();
     105                        }
     106                });
     107            }
     108        });
     109        osmUrl.setLineWrap(true);
     110        osmUrl.setBorder(latlon[0].getBorder());
     111
     112        dlg.add(new JLabel(tr("min lat")), GBC.std().insets(10,20,5,0));
     113        dlg.add(latlon[0], GBC.std().insets(0,20,0,0));
     114        dlg.add(new JLabel(tr("min lon")), GBC.std().insets(10,20,5,0));
     115        dlg.add(latlon[1], GBC.eol().insets(0,20,0,0));
     116        dlg.add(new JLabel(tr("max lat")), GBC.std().insets(10,0,5,0));
     117        dlg.add(latlon[2], GBC.std());
     118        dlg.add(new JLabel(tr("max lon")), GBC.std().insets(10,0,5,0));
     119        dlg.add(latlon[3], GBC.eol());
     120
     121        dlg.add(new JLabel(tr("URL from www.openstreetmap.org")), GBC.eol().insets(10,20,5,0));
     122        dlg.add(osmUrl, GBC.eop().insets(10,0,5,0).fill());
     123        dlg.add(showUrl, GBC.eop().insets(10,0,5,5));
     124        showUrl.setEditable(false);
     125        showUrl.setBackground(dlg.getBackground());
     126        showUrl.addFocusListener(new FocusAdapter(){
     127           @Override
     128            public void focusGained(FocusEvent e) {
     129                showUrl.selectAll();
     130            }
     131        });
     132
     133        gui.tabpane.addTab(tr("Bounding Box"), dlg);
     134    }
     135
     136    /**
     137    * Called when bounding box is changed by one of the other download dialog tabs.
     138    */
     139    public void boundingBoxChanged(DownloadDialog gui) {
     140        updateBboxFields(gui);
     141        updateUrl(gui);
     142    }
     143
     144    private void updateBboxFields(DownloadDialog gui) {
     145        latlon[0].setText(Double.toString(gui.minlat));
     146        latlon[1].setText(Double.toString(gui.minlon));
     147        latlon[2].setText(Double.toString(gui.maxlat));
     148        latlon[3].setText(Double.toString(gui.maxlon));
     149        for (JTextField f : latlon)
     150            f.setCaretPosition(0);
     151    }
     152
     153    private void updateUrl(DownloadDialog gui) {
     154        double lat = (gui.minlat + gui.maxlat)/2;
     155        double lon = (gui.minlon + gui.maxlon)/2;
     156        // convert to mercator (for calculation of zoom only)
     157        double latMin = Math.log(Math.tan(Math.PI/4.0+gui.minlat/180.0*Math.PI/2.0))*180.0/Math.PI;
     158        double latMax = Math.log(Math.tan(Math.PI/4.0+gui.maxlat/180.0*Math.PI/2.0))*180.0/Math.PI;
     159        double size = Math.max(Math.abs(latMax-latMin), Math.abs(gui.maxlon-gui.minlon));
     160        int zoom = 0;
     161        while (zoom <= 20) {
     162            if (size >= 180)
     163                break;
     164            size *= 2;
     165            zoom++;
     166        }
     167        showUrl.setText("http://www.openstreetmap.org/index.html?mlat="+lat+"&mlon="+lon+"&zoom="+zoom);
     168    }
     169
     170    public static Bounds osmurl2bounds(String url) {
     171        int i = url.indexOf('?');
     172        if (i == -1)
     173            return null;
     174        String[] args = url.substring(i+1).split("&");
     175        HashMap<String, String> map = new HashMap<String, String>();
     176        for (String arg : args) {
     177            int eq = arg.indexOf('=');
     178            if (eq != -1) {
     179                map.put(arg.substring(0, eq), arg.substring(eq + 1));
     180            }
     181        }
     182
     183        Bounds b = null;
     184        try {
     185            if (map.containsKey("bbox")) {
     186                String bbox[] = map.get("bbox").split(",");
     187                b = new Bounds(
     188                    new LatLon(Double.parseDouble(bbox[1]), Double.parseDouble(bbox[0])),
     189                    new LatLon(Double.parseDouble(bbox[3]), Double.parseDouble(bbox[2])));
     190
     191            } else {
     192                double size = 180.0 / Math.pow(2, Integer.parseInt(map.get("zoom")));
     193                b = new Bounds(
     194                    new LatLon(parseDouble(map, "lat") - size/2, parseDouble(map, "lon") - size),
     195                    new LatLon(parseDouble(map, "lat") + size/2, parseDouble(map, "lon") + size));
     196            }
     197        } catch (NumberFormatException x) {
     198        } catch (NullPointerException x) {
     199        }
     200        return b;
     201    }
     202
     203    private static double parseDouble(HashMap<String, String> map, String key) {
     204        if (map.containsKey(key))
     205            return Double.parseDouble(map.get(key));
     206        return Double.parseDouble(map.get("m"+key));
    207207    }
    208208}
  • trunk/src/org/openstreetmap/josm/gui/download/DownloadDialog.java

    r1147 r1169  
    3636public class DownloadDialog extends JPanel {
    3737
    38         // the JOptionPane that contains this dialog. required for the closeDialog() method.
    39         private JOptionPane optionPane;
     38    // the JOptionPane that contains this dialog. required for the closeDialog() method.
     39    private JOptionPane optionPane;
    4040
    41         public interface DownloadTask {
    42                 /**
    43                 * Execute the download.
    44                 */
    45                 void download(DownloadAction action, double minlat, double minlon, double maxlat, double maxlon);
     41    public interface DownloadTask {
     42        /**
     43        * Execute the download.
     44        */
     45        void download(DownloadAction action, double minlat, double minlon, double maxlat, double maxlon);
    4646        void loadUrl(boolean newLayer, String url);
    47                 /**
    48                 * @return The checkbox presented to the user
    49                 */
    50                 JCheckBox getCheckBox();
    51                 /**
    52                 * @return The name of the preferences suffix to use for storing the
    53                 * selection state.
    54                 */
    55                 String getPreferencesSuffix();
    56         }
     47        /**
     48        * @return The checkbox presented to the user
     49        */
     50        JCheckBox getCheckBox();
     51        /**
     52        * @return The name of the preferences suffix to use for storing the
     53        * selection state.
     54        */
     55        String getPreferencesSuffix();
     56    }
    5757
    58         /**
    59         * The list of download tasks. First entry should be the osm data entry
    60         * and the second the gps entry. After that, plugins can register additional
    61         * download possibilities.
    62         */
    63         public final List<DownloadTask> downloadTasks = new ArrayList<DownloadTask>(5);
     58    /**
     59    * The list of download tasks. First entry should be the osm data entry
     60    * and the second the gps entry. After that, plugins can register additional
     61    * download possibilities.
     62    */
     63    public final List<DownloadTask> downloadTasks = new ArrayList<DownloadTask>(5);
    6464
    65         public final List<DownloadSelection> downloadSelections = new ArrayList<DownloadSelection>();
    66         public final JTabbedPane tabpane = new JTabbedPane();
    67         public final JCheckBox newLayer;
    68         public final JLabel sizeCheck = new JLabel();
     65    public final List<DownloadSelection> downloadSelections = new ArrayList<DownloadSelection>();
     66    public final JTabbedPane tabpane = new JTabbedPane();
     67    public final JCheckBox newLayer;
     68    public final JLabel sizeCheck = new JLabel();
    6969
    70         public double minlon;
    71         public double minlat;
    72         public double maxlon;
    73         public double maxlat;
     70    public double minlon;
     71    public double minlat;
     72    public double maxlon;
     73    public double maxlat;
    7474
    7575
    76         public DownloadDialog() {
    77                 setLayout(new GridBagLayout());
     76    public DownloadDialog() {
     77        setLayout(new GridBagLayout());
    7878
    79                 downloadTasks.add(new DownloadOsmTask());
    80                 downloadTasks.add(new DownloadGpsTask());
     79        downloadTasks.add(new DownloadOsmTask());
     80        downloadTasks.add(new DownloadGpsTask());
    8181
    82                 // adding the download tasks
    83                 add(new JLabel(tr("Data Sources and Types")), GBC.eol().insets(0,5,0,0));
    84                 for (DownloadTask task : downloadTasks) {
    85                         add(task.getCheckBox(), GBC.eol().insets(20,0,0,0));
    86                         // don't override defaults, if we (initially) don't have any preferences
    87                         if(Main.pref.hasKey("download."+task.getPreferencesSuffix())) {
    88                                 task.getCheckBox().setSelected(Main.pref.getBoolean("download."+task.getPreferencesSuffix()));
    89                         }
    90                 }
     82        // adding the download tasks
     83        add(new JLabel(tr("Data Sources and Types")), GBC.eol().insets(0,5,0,0));
     84        for (DownloadTask task : downloadTasks) {
     85            add(task.getCheckBox(), GBC.eol().insets(20,0,0,0));
     86            // don't override defaults, if we (initially) don't have any preferences
     87            if(Main.pref.hasKey("download."+task.getPreferencesSuffix())) {
     88                task.getCheckBox().setSelected(Main.pref.getBoolean("download."+task.getPreferencesSuffix()));
     89            }
     90        }
    9191
    92                 // predefined download selections
    93                 downloadSelections.add(new BoundingBoxSelection());
    94                 downloadSelections.add(new TileSelection());
    95                 downloadSelections.add(new BookmarkSelection());
    96                 downloadSelections.add(new WorldChooser());
     92        // predefined download selections
     93        downloadSelections.add(new BoundingBoxSelection());
     94        downloadSelections.add(new TileSelection());
     95        downloadSelections.add(new BookmarkSelection());
     96        downloadSelections.add(new WorldChooser());
    9797
    98                 // add selections from plugins
    99                 for (PluginProxy p : Main.plugins) {
    100                         p.addDownloadSelection(downloadSelections);
    101                 }
     98        // add selections from plugins
     99        for (PluginProxy p : Main.plugins) {
     100            p.addDownloadSelection(downloadSelections);
     101        }
    102102
    103                 // now everybody may add their tab to the tabbed pane
    104                 // (not done right away to allow plugins to remove one of
    105                 // the default selectors!)
    106                 for (DownloadSelection s : downloadSelections) {
    107                         s.addGui(this);
    108                 }
     103        // now everybody may add their tab to the tabbed pane
     104        // (not done right away to allow plugins to remove one of
     105        // the default selectors!)
     106        for (DownloadSelection s : downloadSelections) {
     107            s.addGui(this);
     108        }
    109109
    110                 if (Main.map != null) {
    111                         MapView mv = Main.map.mapView;
    112                         minlon = mv.getLatLon(0, mv.getHeight()).lon();
    113                         minlat = mv.getLatLon(0, mv.getHeight()).lat();
    114                         maxlon = mv.getLatLon(mv.getWidth(), 0).lon();
    115                         maxlat = mv.getLatLon(mv.getWidth(), 0).lat();
    116                         boundingBoxChanged(null);
    117                 }
    118                 else if (Main.pref.hasKey("osm-download.bounds")) {
    119                         // read the bounding box from the preferences
    120                         try {
    121                                 String bounds[] = Main.pref.get("osm-download.bounds").split(";");
    122                                 minlat = Double.parseDouble(bounds[0]);
    123                                 minlon = Double.parseDouble(bounds[1]);
    124                                 maxlat = Double.parseDouble(bounds[2]);
    125                                 maxlon = Double.parseDouble(bounds[3]);
    126                                 boundingBoxChanged(null);
    127                         }
    128                         catch (Exception e) {
    129                                 e.printStackTrace();
    130                         }
    131                 }
     110        if (Main.map != null) {
     111            MapView mv = Main.map.mapView;
     112            minlon = mv.getLatLon(0, mv.getHeight()).lon();
     113            minlat = mv.getLatLon(0, mv.getHeight()).lat();
     114            maxlon = mv.getLatLon(mv.getWidth(), 0).lon();
     115            maxlat = mv.getLatLon(mv.getWidth(), 0).lat();
     116            boundingBoxChanged(null);
     117        }
     118        else if (Main.pref.hasKey("osm-download.bounds")) {
     119            // read the bounding box from the preferences
     120            try {
     121                String bounds[] = Main.pref.get("osm-download.bounds").split(";");
     122                minlat = Double.parseDouble(bounds[0]);
     123                minlon = Double.parseDouble(bounds[1]);
     124                maxlat = Double.parseDouble(bounds[2]);
     125                maxlon = Double.parseDouble(bounds[3]);
     126                boundingBoxChanged(null);
     127            }
     128            catch (Exception e) {
     129                e.printStackTrace();
     130            }
     131        }
    132132
    133                 newLayer = new JCheckBox(tr("Download as new layer"), Main.pref.getBoolean("download.newlayer", false));
    134                 add(newLayer, GBC.eol().insets(0,5,0,0));
     133        newLayer = new JCheckBox(tr("Download as new layer"), Main.pref.getBoolean("download.newlayer", false));
     134        add(newLayer, GBC.eol().insets(0,5,0,0));
    135135
    136                 add(new JLabel(tr("Download Area")), GBC.eol().insets(0,5,0,0));
    137                 add(tabpane, GBC.eol().fill());
     136        add(new JLabel(tr("Download Area")), GBC.eol().insets(0,5,0,0));
     137        add(tabpane, GBC.eol().fill());
    138138
    139                 try {
    140                         tabpane.setSelectedIndex(Integer.parseInt(Main.pref.get("download.tab", "0")));
    141                 } catch (Exception ex) {
    142                         Main.pref.put("download.tab", "0");
    143                 }
     139        try {
     140            tabpane.setSelectedIndex(Integer.parseInt(Main.pref.get("download.tab", "0")));
     141        } catch (Exception ex) {
     142            Main.pref.put("download.tab", "0");
     143        }
    144144
    145                 Font labelFont = sizeCheck.getFont();
    146                 sizeCheck.setFont(labelFont.deriveFont(Font.PLAIN, labelFont.getSize()));
    147                 add(sizeCheck, GBC.eop().insets(0,5,5,10));
    148         }
     145        Font labelFont = sizeCheck.getFont();
     146        sizeCheck.setFont(labelFont.deriveFont(Font.PLAIN, labelFont.getSize()));
     147        add(sizeCheck, GBC.eop().insets(0,5,5,10));
     148    }
    149149
    150         private void updateSizeCheck() {
    151                 if ((maxlon-minlon)*(maxlat-minlat) > Main.pref.getDouble("osm-server.max-request-area", 0.25)) {
    152                         sizeCheck.setText(tr("Download area too large; will probably be rejected by server"));
    153                         sizeCheck.setForeground(Color.red);
    154                 } else {
    155                         sizeCheck.setText(tr("Download area ok, size probably acceptable to server"));
    156                         sizeCheck.setForeground(Color.darkGray);
    157                 }
    158         }
     150    private void updateSizeCheck() {
     151        if ((maxlon-minlon)*(maxlat-minlat) > Main.pref.getDouble("osm-server.max-request-area", 0.25)) {
     152            sizeCheck.setText(tr("Download area too large; will probably be rejected by server"));
     153            sizeCheck.setForeground(Color.red);
     154        } else {
     155            sizeCheck.setText(tr("Download area ok, size probably acceptable to server"));
     156            sizeCheck.setForeground(Color.darkGray);
     157        }
     158    }
    159159
    160         /**
    161         * Distributes a "bounding box changed" from one DownloadSelection
    162         * object to the others, so they may update or clear their input
    163         * fields.
    164         *
    165         * @param eventSource - the DownloadSelection object that fired this notification.
    166         */
    167         public void boundingBoxChanged(DownloadSelection eventSource) {
    168                 for (DownloadSelection s : downloadSelections) {
    169                         if (s != eventSource) s.boundingBoxChanged(this);
    170                 }
    171                 updateSizeCheck();
    172         }
     160    /**
     161    * Distributes a "bounding box changed" from one DownloadSelection
     162    * object to the others, so they may update or clear their input
     163    * fields.
     164    *
     165    * @param eventSource - the DownloadSelection object that fired this notification.
     166    */
     167    public void boundingBoxChanged(DownloadSelection eventSource) {
     168        for (DownloadSelection s : downloadSelections) {
     169            if (s != eventSource) s.boundingBoxChanged(this);
     170        }
     171        updateSizeCheck();
     172    }
    173173
    174         /*
    175         * Returns currently selected tab.
    176         */
    177         public int getSelectedTab() {
    178                 return tabpane.getSelectedIndex();
    179         }
     174    /*
     175    * Returns currently selected tab.
     176    */
     177    public int getSelectedTab() {
     178        return tabpane.getSelectedIndex();
     179    }
    180180
    181         /**
    182         * Closes the download dialog. This is intended to be called by one of
    183         * the various download area selection "plugins".
    184         *
    185         * @param download true to download selected data, false to cancel download
    186         */
    187         public void closeDownloadDialog(boolean download) {
    188                 optionPane.setValue(download ? JOptionPane.OK_OPTION : JOptionPane.CANCEL_OPTION);
    189         }
     181    /**
     182    * Closes the download dialog. This is intended to be called by one of
     183    * the various download area selection "plugins".
     184    *
     185    * @param download true to download selected data, false to cancel download
     186    */
     187    public void closeDownloadDialog(boolean download) {
     188        optionPane.setValue(download ? JOptionPane.OK_OPTION : JOptionPane.CANCEL_OPTION);
     189    }
    190190
    191         /**
    192         * Has to be called after this dialog has been added to a JOptionPane.
    193         * @param optionPane
    194         */
    195         public void setOptionPane(JOptionPane optionPane) {
    196                 this.optionPane = optionPane;
    197         }
     191    /**
     192    * Has to be called after this dialog has been added to a JOptionPane.
     193    * @param optionPane
     194    */
     195    public void setOptionPane(JOptionPane optionPane) {
     196        this.optionPane = optionPane;
     197    }
    198198}
  • trunk/src/org/openstreetmap/josm/gui/download/DownloadSelection.java

    r627 r1169  
    44
    55public interface DownloadSelection {
    6         /**
    7          * Add the GUI elements to the dialog.
    8         */
    9         void addGui(DownloadDialog gui);
     6    /**
     7     * Add the GUI elements to the dialog.
     8    */
     9    void addGui(DownloadDialog gui);
    1010
    11         /**
    12         * Update or clear display when a selection is made through another
    13         * DownloadSelection object
    14         */
    15         void boundingBoxChanged(DownloadDialog gui);
    16        
     11    /**
     12    * Update or clear display when a selection is made through another
     13    * DownloadSelection object
     14    */
     15    void boundingBoxChanged(DownloadDialog gui);
     16
    1717}
  • trunk/src/org/openstreetmap/josm/gui/download/TileSelection.java

    r979 r1169  
    1919/**
    2020 * Tile selector.
    21  * 
     21 *
    2222 * Provides a tile coordinate input field.
    23  * 
     23 *
    2424 * @author Frederik Ramm <frederik@remote.org>
    2525 *
     
    2727public class TileSelection implements DownloadSelection {
    2828
    29         private JTextField tileX0 = new JTextField(7);
    30         private JTextField tileY0 = new JTextField(7);
    31         private JTextField tileX1 = new JTextField(7);
    32         private JTextField tileY1 = new JTextField(7);
    33         private JSpinner tileZ = new JSpinner(new SpinnerNumberModel(12, 10, 18, 1));
    34        
    35         public void addGui(final DownloadDialog gui) {
     29    private JTextField tileX0 = new JTextField(7);
     30    private JTextField tileY0 = new JTextField(7);
     31    private JTextField tileX1 = new JTextField(7);
     32    private JTextField tileY1 = new JTextField(7);
     33    private JSpinner tileZ = new JSpinner(new SpinnerNumberModel(12, 10, 18, 1));
    3634
    37                 JPanel smpanel = new JPanel(new GridBagLayout());
    38                 smpanel.add(new JLabel(tr("zoom level")), GBC.std().insets(0,0,10,0));
    39                 smpanel.add(new JLabel(tr("x from")), GBC.std().insets(10,0,5,0));
    40                 smpanel.add(tileX0, GBC.std());
    41                 smpanel.add(new JLabel(tr("to")), GBC.std().insets(10,0,5,0));
    42                 smpanel.add(tileX1, GBC.eol());
    43                 smpanel.add(tileZ, GBC.std().insets(0,0,10,0));
    44                 smpanel.add(new JLabel(tr("y from")), GBC.std().insets(10,0,5,0));
    45                 smpanel.add(tileY0, GBC.std());
    46                 smpanel.add(new JLabel(tr("to")), GBC.std().insets(10,0,5,0));
    47                 smpanel.add(tileY1, GBC.eol());
     35    public void addGui(final DownloadDialog gui) {
    4836
    49                 final FocusListener dialogUpdater = new FocusAdapter() {
    50                         @Override public void focusLost(FocusEvent e) {
    51                                 try {
    52                                         int zoomlvl = (Integer) tileZ.getValue();
    53                                         int fromx = Integer.parseInt(tileX0.getText());
    54                                         int tox = fromx;
    55                                         if (tileX1.getText().length()>0) {
    56                                                 tox = Integer.parseInt(tileX1.getText());
    57                                         }
    58                                         if (tox<fromx) { int i = fromx; fromx=tox; tox=i; }
     37        JPanel smpanel = new JPanel(new GridBagLayout());
     38        smpanel.add(new JLabel(tr("zoom level")), GBC.std().insets(0,0,10,0));
     39        smpanel.add(new JLabel(tr("x from")), GBC.std().insets(10,0,5,0));
     40        smpanel.add(tileX0, GBC.std());
     41        smpanel.add(new JLabel(tr("to")), GBC.std().insets(10,0,5,0));
     42        smpanel.add(tileX1, GBC.eol());
     43        smpanel.add(tileZ, GBC.std().insets(0,0,10,0));
     44        smpanel.add(new JLabel(tr("y from")), GBC.std().insets(10,0,5,0));
     45        smpanel.add(tileY0, GBC.std());
     46        smpanel.add(new JLabel(tr("to")), GBC.std().insets(10,0,5,0));
     47        smpanel.add(tileY1, GBC.eol());
    5948
    60                                         int fromy = Integer.parseInt(tileY0.getText());
    61                                         int toy = fromy;
    62                                         if (tileY1.getText().length()>0) {
    63                                                 toy = Integer.parseInt(tileY1.getText());
    64                                         }
    65                                         if (toy<fromy) { int i = fromy; fromy=toy; toy=i; }
     49        final FocusListener dialogUpdater = new FocusAdapter() {
     50            @Override public void focusLost(FocusEvent e) {
     51                try {
     52                    int zoomlvl = (Integer) tileZ.getValue();
     53                    int fromx = Integer.parseInt(tileX0.getText());
     54                    int tox = fromx;
     55                    if (tileX1.getText().length()>0) {
     56                        tox = Integer.parseInt(tileX1.getText());
     57                    }
     58                    if (tox<fromx) { int i = fromx; fromx=tox; tox=i; }
    6659
    67                                         gui.minlat = tileYToLat(zoomlvl, toy+1);
    68                                         gui.minlon = tileXToLon(zoomlvl, fromx);
    69                                         gui.maxlat = tileYToLat(zoomlvl, fromy);
    70                                         gui.maxlon = tileXToLon(zoomlvl, tox+1);
    71                                         gui.boundingBoxChanged(TileSelection.this);
    72                                         //repaint();
    73                                 } catch (NumberFormatException x) {
    74                                         // ignore
    75                                 }
    76                         }
    77                 };
    78                
    79                 for (JTextField f : new JTextField[] { tileX0, tileX1, tileY0, tileY1 }) {
    80                         f.setMinimumSize(new Dimension(100,new JTextField().getMinimumSize().height));
    81                         f.addFocusListener(dialogUpdater);
    82                 }
     60                    int fromy = Integer.parseInt(tileY0.getText());
     61                    int toy = fromy;
     62                    if (tileY1.getText().length()>0) {
     63                        toy = Integer.parseInt(tileY1.getText());
     64                    }
     65                    if (toy<fromy) { int i = fromy; fromy=toy; toy=i; }
    8366
    84                 gui.tabpane.addTab(tr("Tile Numbers"), smpanel);
    85         }
    86        
    87         /**
    88          * Called when bounding box is changed by one of the other download dialog tabs.
    89          */
    90         public void boundingBoxChanged(DownloadDialog gui) {
    91                 updateBboxFields(gui);
    92         }
    93        
    94         private void updateBboxFields(DownloadDialog gui) {
    95                 int z = ((Integer) tileZ.getValue()).intValue();
    96                 tileX0.setText(Integer.toString(lonToTileX(z, gui.minlon)));
    97                 tileX1.setText(Integer.toString(lonToTileX(z, gui.maxlon-.00001)));
    98                 tileY0.setText(Integer.toString(latToTileY(z, gui.maxlat-.00001)));
    99                 tileY1.setText(Integer.toString(latToTileY(z, gui.minlat)));
    100         }
    101        
    102         public static int latToTileY(int zoom, double lat) {
    103                 if ((zoom < 3) || (zoom > 18)) return -1;
    104                 double l = lat / 180 * Math.PI;
    105                 double pf = Math.log(Math.tan(l) + (1/Math.cos(l)));
    106                 return (int) ((1<<(zoom-1)) * (Math.PI - pf) / Math.PI);
    107         }   
     67                    gui.minlat = tileYToLat(zoomlvl, toy+1);
     68                    gui.minlon = tileXToLon(zoomlvl, fromx);
     69                    gui.maxlat = tileYToLat(zoomlvl, fromy);
     70                    gui.maxlon = tileXToLon(zoomlvl, tox+1);
     71                    gui.boundingBoxChanged(TileSelection.this);
     72                    //repaint();
     73                } catch (NumberFormatException x) {
     74                    // ignore
     75                }
     76            }
     77        };
    10878
    109         public static int lonToTileX(int zoom, double lon) {
    110                 if ((zoom < 3) || (zoom > 18)) return -1;
    111                 return (int) ((1<<(zoom-3)) * (lon + 180.0) / 45.0);
    112         }
     79        for (JTextField f : new JTextField[] { tileX0, tileX1, tileY0, tileY1 }) {
     80            f.setMinimumSize(new Dimension(100,new JTextField().getMinimumSize().height));
     81            f.addFocusListener(dialogUpdater);
     82        }
    11383
    114         public static double tileYToLat(int zoom, int y) {
    115                 if ((zoom < 3) || (zoom > 18)) return Double.MIN_VALUE;
    116                 return Math.atan(Math.sinh(Math.PI - (Math.PI*y / (1<<(zoom-1))))) * 180 / Math.PI;
    117         }
     84        gui.tabpane.addTab(tr("Tile Numbers"), smpanel);
     85    }
    11886
    119         public static double tileXToLon(int zoom, int x) {
    120                 if ((zoom < 3) || (zoom > 18)) return Double.MIN_VALUE;
    121                 return x * 45.0 / (1<<(zoom-3)) - 180.0;
     87    /**
     88     * Called when bounding box is changed by one of the other download dialog tabs.
     89     */
     90    public void boundingBoxChanged(DownloadDialog gui) {
     91        updateBboxFields(gui);
     92    }
    12293
    123         }
     94    private void updateBboxFields(DownloadDialog gui) {
     95        int z = ((Integer) tileZ.getValue()).intValue();
     96        tileX0.setText(Integer.toString(lonToTileX(z, gui.minlon)));
     97        tileX1.setText(Integer.toString(lonToTileX(z, gui.maxlon-.00001)));
     98        tileY0.setText(Integer.toString(latToTileY(z, gui.maxlat-.00001)));
     99        tileY1.setText(Integer.toString(latToTileY(z, gui.minlat)));
     100    }
     101
     102    public static int latToTileY(int zoom, double lat) {
     103        if ((zoom < 3) || (zoom > 18)) return -1;
     104        double l = lat / 180 * Math.PI;
     105        double pf = Math.log(Math.tan(l) + (1/Math.cos(l)));
     106        return (int) ((1<<(zoom-1)) * (Math.PI - pf) / Math.PI);
     107    }
     108
     109    public static int lonToTileX(int zoom, double lon) {
     110        if ((zoom < 3) || (zoom > 18)) return -1;
     111        return (int) ((1<<(zoom-3)) * (lon + 180.0) / 45.0);
     112    }
     113
     114    public static double tileYToLat(int zoom, int y) {
     115        if ((zoom < 3) || (zoom > 18)) return Double.MIN_VALUE;
     116        return Math.atan(Math.sinh(Math.PI - (Math.PI*y / (1<<(zoom-1))))) * 180 / Math.PI;
     117    }
     118
     119    public static double tileXToLon(int zoom, int x) {
     120        if ((zoom < 3) || (zoom > 18)) return Double.MIN_VALUE;
     121        return x * 45.0 / (1<<(zoom-3)) - 180.0;
     122
     123    }
    124124}
  • trunk/src/org/openstreetmap/josm/gui/download/WorldChooser.java

    r733 r1169  
    3838public class WorldChooser extends NavigatableComponent implements DownloadSelection {
    3939
    40         /**
    41         * The world as picture.
    42         */
    43         private ImageIcon world;
     40    /**
     41    * The world as picture.
     42    */
     43    private ImageIcon world;
    4444
    45         /**
    46         * Maximum scale level
    47         */
    48         private double scaleMax;
     45    /**
     46    * Maximum scale level
     47    */
     48    private double scaleMax;
    4949
    50         /**
    51         * Mark this rectangle (lat/lon values) when painting.
    52         */
    53         private EastNorth markerMin, markerMax;
     50    /**
     51    * Mark this rectangle (lat/lon values) when painting.
     52    */
     53    private EastNorth markerMin, markerMax;
    5454
    55         private Projection projection;
     55    private Projection projection;
    5656
    57         /**
    58         * Create the chooser component.
    59         */
    60         public WorldChooser() {
    61                 URL path = Main.class.getResource("/images/world.jpg");
    62                 world = new ImageIcon(path);
    63                 center = new EastNorth(world.getIconWidth()/2, world.getIconHeight()/2);
    64                 setPreferredSize(new Dimension(400, 200));
     57    /**
     58    * Create the chooser component.
     59    */
     60    public WorldChooser() {
     61        URL path = Main.class.getResource("/images/world.jpg");
     62        world = new ImageIcon(path);
     63        center = new EastNorth(world.getIconWidth()/2, world.getIconHeight()/2);
     64        setPreferredSize(new Dimension(400, 200));
    6565
    66                 projection = new Projection() {
    67                         public EastNorth latlon2eastNorth(LatLon p) {
    68                                 return new EastNorth(
    69                                                 (p.lon()+180) / 360 * world.getIconWidth(),
    70                                                 (p.lat()+90) / 180 * world.getIconHeight());
    71                         }
    72                         public LatLon eastNorth2latlon(EastNorth p) {
    73                                 return new LatLon(
    74                                                 p.north()*180/world.getIconHeight() - 90,
    75                                                 p.east()*360/world.getIconWidth() - 180);
    76                         }
    77                         @Override public String toString() {
    78                                 return "WorldChooser";
    79                         }
     66        projection = new Projection() {
     67            public EastNorth latlon2eastNorth(LatLon p) {
     68                return new EastNorth(
     69                        (p.lon()+180) / 360 * world.getIconWidth(),
     70                        (p.lat()+90) / 180 * world.getIconHeight());
     71            }
     72            public LatLon eastNorth2latlon(EastNorth p) {
     73                return new LatLon(
     74                        p.north()*180/world.getIconHeight() - 90,
     75                        p.east()*360/world.getIconWidth() - 180);
     76            }
     77            @Override public String toString() {
     78                return "WorldChooser";
     79            }
    8080            public String getCacheDirectoryName() {
    8181                throw new UnsupportedOperationException();
    8282            }
    83                         public double scaleFactor() {
    84                     return 1.0 / world.getIconWidth();
     83            public double scaleFactor() {
     84                return 1.0 / world.getIconWidth();
    8585            }
    8686
    87                 };
     87        };
    8888
    89                 MapScaler scaler = new MapScaler(this, projection);
    90                 add(scaler);
    91                 scaler.setLocation(10,10);
     89        MapScaler scaler = new MapScaler(this, projection);
     90        add(scaler);
     91        scaler.setLocation(10,10);
    9292
    93                 setMinimumSize(new Dimension(350, 350/2));
    94         }
    95 
    96         public void addGui(final DownloadDialog gui) {
    97                 JPanel temp = new JPanel();
    98                 temp.setLayout(new BorderLayout());
    99                 temp.add(this, BorderLayout.CENTER);
    100                 temp.add(new JLabel(tr("You can use the mouse or Ctrl+Arrow keys/./ to zoom and pan.")), BorderLayout.SOUTH);
    101                 gui.tabpane.add(temp, tr("Map"));
    102                 new MapMover(this, temp);
    103                 SelectionEnded selListener = new SelectionEnded(){
    104                         public void selectionEnded(Rectangle r, boolean alt, boolean shift, boolean ctrl) {
    105                                 markerMin = getEastNorth(r.x, r.y+r.height);
    106                                 markerMax = getEastNorth(r.x+r.width, r.y);
    107                                 LatLon min = getProjection().eastNorth2latlon(markerMin);
    108                                 LatLon max = getProjection().eastNorth2latlon(markerMax);
    109                                 gui.minlat = min.lat();
    110                                 gui.minlon = min.lon();
    111                                 gui.maxlat = max.lat();
    112                                 gui.maxlon = max.lon();
    113                                 gui.boundingBoxChanged(WorldChooser.this);
    114                                 repaint();
    115                         }
    116                         public void addPropertyChangeListener(PropertyChangeListener listener) {}
    117                         public void removePropertyChangeListener(PropertyChangeListener listener) {}
    118                 };
    119                 SelectionManager sm = new SelectionManager(selListener, false, this);
    120                 sm.register(this);
     93        setMinimumSize(new Dimension(350, 350/2));
    12194    }
    12295
    123         public void boundingBoxChanged(DownloadDialog gui) {
    124                 markerMin = getProjection().latlon2eastNorth(new LatLon(gui.minlat, gui.minlon));
    125                 markerMax = getProjection().latlon2eastNorth(new LatLon(gui.maxlat, gui.maxlon));
    126                 repaint();
    127         }
     96    public void addGui(final DownloadDialog gui) {
     97        JPanel temp = new JPanel();
     98        temp.setLayout(new BorderLayout());
     99        temp.add(this, BorderLayout.CENTER);
     100        temp.add(new JLabel(tr("You can use the mouse or Ctrl+Arrow keys/./ to zoom and pan.")), BorderLayout.SOUTH);
     101        gui.tabpane.add(temp, tr("Map"));
     102        new MapMover(this, temp);
     103        SelectionEnded selListener = new SelectionEnded(){
     104            public void selectionEnded(Rectangle r, boolean alt, boolean shift, boolean ctrl) {
     105                markerMin = getEastNorth(r.x, r.y+r.height);
     106                markerMax = getEastNorth(r.x+r.width, r.y);
     107                LatLon min = getProjection().eastNorth2latlon(markerMin);
     108                LatLon max = getProjection().eastNorth2latlon(markerMax);
     109                gui.minlat = min.lat();
     110                gui.minlon = min.lon();
     111                gui.maxlat = max.lat();
     112                gui.maxlon = max.lon();
     113                gui.boundingBoxChanged(WorldChooser.this);
     114                repaint();
     115            }
     116            public void addPropertyChangeListener(PropertyChangeListener listener) {}
     117            public void removePropertyChangeListener(PropertyChangeListener listener) {}
     118        };
     119        SelectionManager sm = new SelectionManager(selListener, false, this);
     120        sm.register(this);
     121    }
    128122
    129         /**
    130          * Set the scale as well as the preferred size.
    131          */
    132         @Override public void setPreferredSize(Dimension preferredSize) {
    133                 super.setPreferredSize(preferredSize);
    134                 scale = world.getIconWidth()/preferredSize.getWidth();
    135                 scaleMax = scale;
    136         }
     123    public void boundingBoxChanged(DownloadDialog gui) {
     124        markerMin = getProjection().latlon2eastNorth(new LatLon(gui.minlat, gui.minlon));
     125        markerMax = getProjection().latlon2eastNorth(new LatLon(gui.maxlat, gui.maxlon));
     126        repaint();
     127    }
    137128
    138         /**
    139          * Draw the current selected region.
    140          */
    141         @Override public void paint(Graphics g) {
    142                 EastNorth tl = getEastNorth(0,0);
    143                 EastNorth br = getEastNorth(getWidth(),getHeight());
    144                 g.drawImage(world.getImage(),0,0,getWidth(),getHeight(),(int)tl.east(),(int)tl.north(),(int)br.east(),(int)br.north(), null);
     129    /**
     130     * Set the scale as well as the preferred size.
     131     */
     132    @Override public void setPreferredSize(Dimension preferredSize) {
     133        super.setPreferredSize(preferredSize);
     134        scale = world.getIconWidth()/preferredSize.getWidth();
     135        scaleMax = scale;
     136    }
    145137
    146                 // draw marker rect
    147                 if (markerMin != null && markerMax != null) {
    148                         Point p1 = getPoint(markerMin);
    149                         Point p2 = getPoint(markerMax);
    150                         double x = Math.min(p1.x, p2.x);
    151                         double y = Math.min(p1.y, p2.y);
    152                         double w = Math.max(p1.x, p2.x) - x;
    153                         double h = Math.max(p1.y, p2.y) - y;
    154                         if (w < 1)
    155                                 w = 1;
    156                         if (h < 1)
    157                                 h = 1;
    158                         g.setColor(Color.YELLOW);
    159                         g.drawRect((int)x, (int)y, (int)w, (int)h);
    160                 }
    161                 super.paint(g);
    162         }
     138    /**
     139     * Draw the current selected region.
     140     */
     141    @Override public void paint(Graphics g) {
     142        EastNorth tl = getEastNorth(0,0);
     143        EastNorth br = getEastNorth(getWidth(),getHeight());
     144        g.drawImage(world.getImage(),0,0,getWidth(),getHeight(),(int)tl.east(),(int)tl.north(),(int)br.east(),(int)br.north(), null);
    163145
    164         @Override public void zoomTo(EastNorth newCenter, double scale) {
    165                 if (getWidth() != 0 && scale > scaleMax) {
    166                         scale = scaleMax;
    167                         newCenter = center;
    168                 }
    169                 super.zoomTo(newCenter, scale);
    170         }
     146        // draw marker rect
     147        if (markerMin != null && markerMax != null) {
     148            Point p1 = getPoint(markerMin);
     149            Point p2 = getPoint(markerMax);
     150            double x = Math.min(p1.x, p2.x);
     151            double y = Math.min(p1.y, p2.y);
     152            double w = Math.max(p1.x, p2.x) - x;
     153            double h = Math.max(p1.y, p2.y) - y;
     154            if (w < 1)
     155                w = 1;
     156            if (h < 1)
     157                h = 1;
     158            g.setColor(Color.YELLOW);
     159            g.drawRect((int)x, (int)y, (int)w, (int)h);
     160        }
     161        super.paint(g);
     162    }
    171163
    172         /**
    173          * Always use our image projection mode.
    174          */
    175         @Override protected Projection getProjection() {
    176                 return projection;
    177         }
     164    @Override public void zoomTo(EastNorth newCenter, double scale) {
     165        if (getWidth() != 0 && scale > scaleMax) {
     166            scale = scaleMax;
     167            newCenter = center;
     168        }
     169        super.zoomTo(newCenter, scale);
     170    }
     171
     172    /**
     173     * Always use our image projection mode.
     174     */
     175    @Override protected Projection getProjection() {
     176        return projection;
     177    }
    178178}
  • trunk/src/org/openstreetmap/josm/gui/layer/DataChangeListener.java

    r627 r1169  
    44public interface DataChangeListener {
    55
    6         public void dataChanged(OsmDataLayer l);
    7        
     6    public void dataChanged(OsmDataLayer l);
     7
    88}
  • trunk/src/org/openstreetmap/josm/gui/layer/GeoImageLayer.java

    r1152 r1169  
    7878public class GeoImageLayer extends Layer {
    7979
    80         private static final class ImageEntry implements Comparable<ImageEntry> {
    81                 File image;
    82                 Date time;
    83                 LatLon coor;
    84                 EastNorth pos;
    85                 Icon icon;
    86                 public int compareTo(ImageEntry image) {
    87                         return time.compareTo(image.time);
    88                 }
    89         }
    90 
    91         private static final class Loader extends PleaseWaitRunnable {
    92                 boolean cancelled = false;
    93                 private GeoImageLayer layer;
    94                 private final Collection<File> files;
    95                 private final GpxLayer gpxLayer;
    96                 public Loader(Collection<File> files, GpxLayer gpxLayer) {
    97                         super(tr("Images for {0}", gpxLayer.name));
    98                         this.files = files;
    99                         this.gpxLayer = gpxLayer;
    100                 }
    101                 @Override protected void realRun() throws IOException {
    102                         Main.pleaseWaitDlg.currentAction.setText(tr("Read GPX..."));
    103                         LinkedList<TimedPoint> gps = new LinkedList<TimedPoint>();
    104 
    105                         // Extract dates and locations from GPX input
    106 
    107                         for (GpxTrack trk : gpxLayer.data.tracks) {
    108                                 for (Collection<WayPoint> segment : trk.trackSegs) {
    109                                         for (WayPoint p : segment) {
    110                                         if (!p.attr.containsKey("time"))
    111                                                 throw new IOException(tr("No time for point {0} x {1}",p.latlon.lat(),p.latlon.lon()));
    112                                         Date d = null;
    113                                         try {
    114                                                 d = DateParser.parse((String) p.attr.get("time"));
    115                                         } catch (ParseException e) {
    116                                                 throw new IOException(tr("Cannot read time \"{0}\" from point {1} x {2}",p.attr.get("time"),p.latlon.lat(),p.latlon.lon()));
    117                                         }
    118                                         gps.add(new TimedPoint(d, p.eastNorth));
    119                                 }
    120                         }
    121                         }
    122 
    123                         if (gps.isEmpty()) {
    124                                 errorMessage = tr("No images with readable timestamps found.");
    125                                 return;
    126                         }
    127 
    128                         // read the image files
    129                         ArrayList<ImageEntry> data = new ArrayList<ImageEntry>(files.size());
    130                         int i = 0;
    131                         Main.pleaseWaitDlg.progress.setMaximum(files.size());
    132                         for (File f : files) {
    133                                 if (cancelled)
    134                                         break;
    135                                 Main.pleaseWaitDlg.currentAction.setText(tr("Reading {0}...",f.getName()));
    136                                 Main.pleaseWaitDlg.progress.setValue(i++);
    137 
    138                                 ImageEntry e = new ImageEntry();
    139                                 try {
    140                                         e.time = ExifReader.readTime(f);
    141                                 } catch (ParseException e1) {
    142                                         continue;
    143                                 }
    144                                 if (e.time == null)
    145                                         continue;
    146                                 e.image = f;
    147                                 e.icon = loadScaledImage(f, 16);
    148 
    149                                 data.add(e);
    150                         }
    151                         layer = new GeoImageLayer(data, gps);
    152                         layer.calculatePosition();
    153                 }
    154                 @Override protected void finish() {
    155                         if (layer != null)
    156                                 Main.main.addLayer(layer);
    157                 }
    158                 @Override protected void cancel() {cancelled = true;}
    159         }
    160 
    161         public ArrayList<ImageEntry> data;
    162         private LinkedList<TimedPoint> gps = new LinkedList<TimedPoint>();
    163 
    164         /**
    165         * The delta added to all timestamps in files from the camera
    166         * to match to the timestamp from the gps receivers tracklog.
    167         */
    168         private long delta = Long.parseLong(Main.pref.get("tagimages.delta", "0"));
    169         private long gpstimezone = Long.parseLong(Main.pref.get("tagimages.gpstimezone", "0"))*60*60*1000;
    170         private boolean mousePressed = false;
    171         private static final SimpleDateFormat dateFormat = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss");
    172         private MouseAdapter mouseAdapter;
    173         private int currentImage;
    174 
    175         public static final class GpsTimeIncorrect extends Exception {
    176                 public GpsTimeIncorrect(String message, Throwable cause) {
    177                         super(message, cause);
    178                 }
    179                 public GpsTimeIncorrect(String message) {
    180                         super(message);
    181                 }
    182         }
    183 
    184         private static final class TimedPoint implements Comparable<TimedPoint> {
    185                 Date time;
    186                 EastNorth pos;
    187                 public TimedPoint(Date time, EastNorth pos) {
    188                         this.time = time;
    189                         this.pos = pos;
    190                 }
    191                 public int compareTo(TimedPoint point) {
    192                         return time.compareTo(point.time);
    193                 }
    194         }
    195 
    196         public static void create(Collection<File> files, GpxLayer gpxLayer) {
    197                 Loader loader = new Loader(files, gpxLayer);
    198                 Main.worker.execute(loader);
    199         }
    200 
    201         private GeoImageLayer(final ArrayList<ImageEntry> data, LinkedList<TimedPoint> gps) {
    202                 super(tr("Geotagged Images"));
    203                 Collections.sort(data);
    204                 Collections.sort(gps);
    205                 this.data = data;
    206                 this.gps = gps;
    207                 mouseAdapter = new MouseAdapter(){
    208                         @Override public void mousePressed(MouseEvent e) {
    209                                 if (e.getButton() != MouseEvent.BUTTON1)
    210                                         return;
    211                                 mousePressed  = true;
    212                                 if (visible)
    213                                         Main.map.mapView.repaint();
    214                         }
    215                         @Override public void mouseReleased(MouseEvent ev) {
    216                                 if (ev.getButton() != MouseEvent.BUTTON1)
    217                                         return;
    218                                 mousePressed = false;
    219                                 if (!visible)
    220                                         return;
    221                                 for (int i = data.size(); i > 0; --i) {
    222                                         ImageEntry e = data.get(i-1);
    223                                         if (e.pos == null)
    224                                                 continue;
    225                                         Point p = Main.map.mapView.getPoint(e.pos);
    226                                         Rectangle r = new Rectangle(p.x-e.icon.getIconWidth()/2, p.y-e.icon.getIconHeight()/2, e.icon.getIconWidth(), e.icon.getIconHeight());
    227                                         if (r.contains(ev.getPoint())) {
    228                                                 showImage(i-1);
    229                                                 break;
    230                                         }
    231                                 }
    232                                 Main.map.mapView.repaint();
    233                         }
    234                 };
    235                 Main.map.mapView.addMouseListener(mouseAdapter);
    236                 Layer.listeners.add(new LayerChangeListener(){
    237                         public void activeLayerChange(Layer oldLayer, Layer newLayer) {}
    238                         public void layerAdded(Layer newLayer) {}
    239                         public void layerRemoved(Layer oldLayer) {
    240                                 Main.map.mapView.removeMouseListener(mouseAdapter);
    241                         }
    242                 });
    243         }
    244 
    245         private void showImage(int i) {
    246                 currentImage = i;
    247                 final JPanel p = new JPanel(new BorderLayout());
    248                 final ImageEntry e = data.get(currentImage);
    249                 final JScrollPane scroll = new JScrollPane(new JLabel(loadScaledImage(e.image, 580)));
    250                 final JViewport vp = scroll.getViewport();
    251                 p.add(scroll, BorderLayout.CENTER);
    252 
    253                 final JToggleButton scale = new JToggleButton(ImageProvider.get("dialogs", "zoom-best-fit"));
    254                 final JButton next  = new JButton(ImageProvider.get("dialogs", "next"));
    255                 final JButton prev = new JButton(ImageProvider.get("dialogs", "previous"));
    256                 final JToggleButton cent = new JToggleButton(ImageProvider.get("dialogs", "centreview"));
    257 
    258                 JPanel p2 = new JPanel();
    259                 p2.add(prev);
    260                 p2.add(scale);
    261                 p2.add(cent);
    262                 p2.add(next);
    263                 prev.setEnabled(currentImage>0?true:false);
    264                 next.setEnabled(currentImage<data.size()-1?true:false);
    265                 p.add(p2, BorderLayout.SOUTH);
    266                 final JOptionPane pane = new JOptionPane(p, JOptionPane.PLAIN_MESSAGE);
    267                 final JDialog dlg = pane.createDialog(Main.parent, e.image+" ("+e.coor.toDisplayString()+")");
    268                 scale.addActionListener(new ActionListener(){
    269                         public void actionPerformed(ActionEvent ev) {
    270                                 p.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
    271                                 if (scale.getModel().isSelected())
    272                                         ((JLabel)vp.getView()).setIcon(loadScaledImage(e.image, Math.max(vp.getWidth(), vp.getHeight())));
    273                                 else
    274                                         ((JLabel)vp.getView()).setIcon(new ImageIcon(e.image.getPath()));
    275                                 p.setCursor(Cursor.getDefaultCursor());
    276                         }
    277                 });
    278                 scale.setSelected(true);
    279                 cent.addActionListener(new ActionListener(){
    280                         public void actionPerformed(ActionEvent ev) {
    281                                 final ImageEntry e = data.get(currentImage);
    282                                 if (cent.getModel().isSelected())
    283                                         Main.map.mapView.zoomTo(e.pos, Main.map.mapView.getScale());
    284                         }
    285                 });
    286 
    287                 ActionListener nextprevAction = new ActionListener(){
    288                         public void actionPerformed(ActionEvent ev) {                       
    289                                 p.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
    290                                 if (ev.getActionCommand().equals("Next")) {
    291                                         currentImage++;
    292                                         if(currentImage>=data.size()-1) next.setEnabled(false);
    293                                         prev.setEnabled(true);
    294                                 } else {
    295                                         currentImage--;
    296                                         if(currentImage<=0) prev.setEnabled(false);
    297                                         next.setEnabled(true);
    298                                 }
    299 
    300                                 final ImageEntry e = data.get(currentImage);
    301                                 if (scale.getModel().isSelected())
    302                                         ((JLabel)vp.getView()).setIcon(loadScaledImage(e.image, Math.max(vp.getWidth(), vp.getHeight())));
    303                                 else
    304                                         ((JLabel)vp.getView()).setIcon(new ImageIcon(e.image.getPath()));
    305                                 dlg.setTitle(e.image+" ("+e.coor.toDisplayString()+")");
    306                                 if (cent.getModel().isSelected())
    307                                         Main.map.mapView.zoomTo(e.pos, Main.map.mapView.getScale());
    308                                 p.setCursor(Cursor.getDefaultCursor());
    309                         }
    310                 };
    311                 next.setActionCommand("Next");
    312                 prev.setActionCommand("Previous");
    313                 next.setMnemonic(KeyEvent.VK_RIGHT);
    314                 prev.setMnemonic(KeyEvent.VK_LEFT);
    315                 scale.setMnemonic(KeyEvent.VK_F);
    316                 cent.setMnemonic(KeyEvent.VK_C);
    317                 next.setToolTipText("Show next image");
    318                 prev.setToolTipText("Show previous image");
    319                 cent.setToolTipText("Centre image location in main display");
    320                 scale.setToolTipText("Scale image to fit");
    321 
    322                 prev.addActionListener(nextprevAction);
    323                 next.addActionListener(nextprevAction);
    324                 cent.setSelected(false);
    325 
    326                 dlg.addComponentListener(new ComponentListener() {
    327                         boolean ignoreEvent = true;
    328                         public void componentHidden(ComponentEvent e) {}
    329                         public void componentMoved(ComponentEvent e) {}
    330                         public void componentResized(ComponentEvent ev) {
    331                                 // we ignore the first resize event, as the picture is scaled already on load:
    332                                 if (scale.getModel().isSelected() && !ignoreEvent) {
    333                                         ((JLabel)vp.getView()).setIcon(loadScaledImage(e.image, Math.max(vp.getWidth(), vp.getHeight())));
    334                                 }
    335                                 ignoreEvent = false;
    336             }
    337                         public void componentShown(ComponentEvent e) {}
    338                        
    339                 });
    340                 dlg.setModal(false);
    341                 dlg.setVisible(true);
    342                 dlg.setResizable(true);
    343         }
    344 
    345         @Override public Icon getIcon() {
    346                 return ImageProvider.get("layer", "tagimages_small");
    347         }
    348 
    349         @Override public Object getInfoComponent() {
    350                 JPanel p = new JPanel(new GridBagLayout());
    351                 p.add(new JLabel(getToolTipText()), GBC.eop());
    352 
    353                 p.add(new JLabel(tr("GPS start: {0}",dateFormat.format(gps.getFirst().time))), GBC.eol());
    354                 p.add(new JLabel(tr("GPS end: {0}",dateFormat.format(gps.getLast().time))), GBC.eop());
    355 
    356                 p.add(new JLabel(tr("current delta: {0}s",(delta/1000.0))), GBC.eol());
    357                 p.add(new JLabel(tr("timezone difference: ")+(gpstimezone>0?"+":"")+(gpstimezone/1000/60/60)), GBC.eop());
    358 
    359                 JList img = new JList(data.toArray());
    360                 img.setCellRenderer(new DefaultListCellRenderer(){
    361                         @Override public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
    362                                 super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
    363                                 ImageEntry e = (ImageEntry)value;
    364                                 setIcon(e.icon);
    365                                 setText(e.image.getName()+" ("+dateFormat.format(new Date(e.time.getTime()+(delta+gpstimezone)))+")");
    366                                 if (e.pos == null)
    367                                         setForeground(Color.red);
    368                                 return this;
    369                         }
    370                 });
    371                 img.setVisibleRowCount(5);
    372                 p.add(new JScrollPane(img), GBC.eop().fill(GBC.BOTH));
    373                 return p;
    374         }
    375 
    376         @Override public String getToolTipText() {
    377                 int i = 0;
    378                 for (ImageEntry e : data)
    379                         if (e.pos != null)
    380                                 i++;
    381                 return data.size()+" "+trn("image","images",data.size())+". "+tr("{0} within the track.",i);
    382         }
    383 
    384         @Override public boolean isMergable(Layer other) {
    385                 return other instanceof GeoImageLayer;
    386         }
    387 
    388         @Override public void mergeFrom(Layer from) {
    389                 GeoImageLayer l = (GeoImageLayer)from;
    390                 data.addAll(l.data);
    391         }
    392 
    393         @Override public void paint(Graphics g, MapView mv) {
    394                 boolean clickedFound = false;
    395                 for (ImageEntry e : data) {
    396                         if (e.pos != null) {
    397                                 Point p = mv.getPoint(e.pos);
    398                                 Rectangle r = new Rectangle(p.x-e.icon.getIconWidth()/2, p.y-e.icon.getIconHeight()/2, e.icon.getIconWidth(), e.icon.getIconHeight());
    399                                 e.icon.paintIcon(mv, g, r.x, r.y);
    400                                 Border b = null;
    401                                 Point mousePosition = mv.getMousePosition();
    402                                 if (mousePosition == null)
    403                                         continue; // mouse outside the whole window
    404                                 if (!clickedFound && mousePressed && r.contains(mousePosition)) {
    405                                         b = BorderFactory.createBevelBorder(BevelBorder.LOWERED);
    406                                         clickedFound = true;
    407                                 } else
    408                                         b = BorderFactory.createBevelBorder(BevelBorder.RAISED);
    409                                 Insets inset = b.getBorderInsets(mv);
    410                                 r.grow((inset.top+inset.bottom)/2, (inset.left+inset.right)/2);
    411                                 b.paintBorder(mv, g, r.x, r.y, r.width, r.height);
    412                         }
    413                 }
    414         }
    415 
    416         @Override public void visitBoundingBox(BoundingXYVisitor v) {
    417                 for (ImageEntry e : data)
    418                         v.visit(e.pos);
    419         }
    420 
    421         @Override public Component[] getMenuEntries() {
    422                 JMenuItem sync = new JMenuItem(tr("Sync clock"), ImageProvider.get("clock"));
    423                 sync.addActionListener(new ActionListener(){
    424                         public void actionPerformed(ActionEvent e) {
    425                                 JFileChooser fc = new JFileChooser(Main.pref.get("tagimages.lastdirectory"));
    426                                 fc.setFileSelectionMode(JFileChooser.FILES_ONLY);
    427                                 fc.setAcceptAllFileFilterUsed(false);
    428                                 fc.setFileFilter(new FileFilter(){
    429                                         @Override public boolean accept(File f) {
    430                                                 return f.isDirectory() || f.getName().toLowerCase().endsWith(".jpg");
    431                                         }
    432                                         @Override public String getDescription() {
    433                                                 return tr("JPEG images (*.jpg)");
    434                                         }
    435                                 });
    436                                 fc.showOpenDialog(Main.parent);
    437                                 File sel = fc.getSelectedFile();
    438                                 if (sel == null)
    439                                         return;
    440                                 Main.pref.put("tagimages.lastdirectory", sel.getPath());
    441                                 sync(sel);
    442                                 Main.map.repaint();
    443                         }
    444                 });
    445                 return new Component[]{
    446                                 new JMenuItem(new LayerListDialog.ShowHideLayerAction(this)),
    447                                 new JMenuItem(new LayerListDialog.DeleteLayerAction(this)),
    448                                 new JSeparator(),
    449                                 sync,
    450                                 new JSeparator(),
    451                                 new JMenuItem(new RenameLayerAction(null, this)),
    452                                 new JSeparator(),
    453                                 new JMenuItem(new LayerListPopup.InfoAction(this))};
    454         }
    455 
    456         private void calculatePosition() {
    457                 for (ImageEntry e : data) {
    458                         TimedPoint lastTP = null;
    459                         for (TimedPoint tp : gps) {
    460                                 Date time = new Date(tp.time.getTime() - (delta+gpstimezone));
    461                                 if (time.after(e.time) && lastTP != null) {
    462                                         double x = (lastTP.pos.east()+tp.pos.east())/2;
    463                                         double y = (lastTP.pos.north()+tp.pos.north())/2;
    464                                         e.pos = new EastNorth(x,y);
    465                                         break;
    466                                 }
    467                                 lastTP = tp;
    468                         }
    469                         if (e.pos != null)
    470                                 e.coor = Main.proj.eastNorth2latlon(e.pos);
    471                 }
    472         }
    473 
    474         private void sync(File f) {
    475                 Date exifDate;
    476                 try {
    477                         exifDate = ExifReader.readTime(f);
    478                 } catch (ParseException e) {
    479                         JOptionPane.showMessageDialog(Main.parent, tr("The date in file \"{0}\" could not be parsed.", f.getName()));
    480                         return;
    481                 }
    482                 if (exifDate == null) {
    483                         JOptionPane.showMessageDialog(Main.parent, tr("There is no EXIF time within the file \"{0}\".", f.getName()));
    484                         return;
    485                 }
    486                 JPanel p = new JPanel(new GridBagLayout());
    487                 p.add(new JLabel(tr("Image")), GBC.eol());
    488                 p.add(new JLabel(loadScaledImage(f, 300)), GBC.eop());
    489                 p.add(new JLabel(tr("Enter shown date (mm/dd/yyyy HH:MM:SS)")), GBC.eol());
    490                 JTextField gpsText = new JTextField(dateFormat.format(new Date(exifDate.getTime()+delta)));
    491                 p.add(gpsText, GBC.eol().fill(GBC.HORIZONTAL));
    492                 p.add(new JLabel(tr("GPS unit timezone (difference to photo)")), GBC.eol());
    493                 String t = Main.pref.get("tagimages.gpstimezone", "0");
    494                 if (t.charAt(0) != '-')
    495                         t = "+"+t;
    496                 JTextField gpsTimezone = new JTextField(t);
    497                 p.add(gpsTimezone, GBC.eol().fill(GBC.HORIZONTAL));
    498 
    499                 while (true) {
    500                         int answer = JOptionPane.showConfirmDialog(Main.parent, p, tr("Syncronize Time with GPS Unit"), JOptionPane.OK_CANCEL_OPTION);
    501                         if (answer != JOptionPane.OK_OPTION || gpsText.getText().equals(""))
    502                                 return;
    503                         try {
    504                                 delta = DateParser.parse(gpsText.getText()).getTime() - exifDate.getTime();
    505                                 String time = gpsTimezone.getText();
    506                                 if (!time.equals("") && time.charAt(0) == '+')
    507                                         time = time.substring(1);
    508                                 if (time.equals(""))
    509                                         time = "0";
    510                                 gpstimezone = Long.valueOf(time)*60*60*1000;
    511                                 Main.pref.put("tagimages.delta", ""+delta);
    512                                 Main.pref.put("tagimages.gpstimezone", time);
    513                                 calculatePosition();
    514                                 return;
    515                         } catch (NumberFormatException x) {
    516                                 JOptionPane.showMessageDialog(Main.parent, tr("Time entered could not be parsed."));
    517                         } catch (ParseException x) {
    518                                 JOptionPane.showMessageDialog(Main.parent, tr("Time entered could not be parsed."));
    519                         }
    520                 }
    521         }
    522 
    523         private static Icon loadScaledImage(File f, int maxSize) {
    524                 Image img = new ImageIcon(f.getPath()).getImage();
    525                 int w = img.getWidth(null);
    526                 int h = img.getHeight(null);
    527                 if (w>h) {
    528                         h = Math.round(maxSize*((float)h/w));
    529                         w = maxSize;
    530                 } else {
    531                         w = Math.round(maxSize*((float)w/h));
    532                         h = maxSize;
    533                 }
    534                 return new ImageIcon(createResizedCopy(img, w, h));
    535         }
    536        
    537         private static BufferedImage createResizedCopy(Image originalImage,
    538                         int scaledWidth, int scaledHeight)
    539         {
    540                 BufferedImage scaledBI = new BufferedImage(scaledWidth, scaledHeight, BufferedImage.TYPE_INT_RGB);
    541                 Graphics2D g = scaledBI.createGraphics();
    542 
    543                 g.drawImage(originalImage, 0, 0, scaledWidth, scaledHeight, null);
    544                 g.dispose();
    545                 return scaledBI;
    546         }
     80    private static final class ImageEntry implements Comparable<ImageEntry> {
     81        File image;
     82        Date time;
     83        LatLon coor;
     84        EastNorth pos;
     85        Icon icon;
     86        public int compareTo(ImageEntry image) {
     87            return time.compareTo(image.time);
     88        }
     89    }
     90
     91    private static final class Loader extends PleaseWaitRunnable {
     92        boolean cancelled = false;
     93        private GeoImageLayer layer;
     94        private final Collection<File> files;
     95        private final GpxLayer gpxLayer;
     96        public Loader(Collection<File> files, GpxLayer gpxLayer) {
     97            super(tr("Images for {0}", gpxLayer.name));
     98            this.files = files;
     99            this.gpxLayer = gpxLayer;
     100        }
     101        @Override protected void realRun() throws IOException {
     102            Main.pleaseWaitDlg.currentAction.setText(tr("Read GPX..."));
     103            LinkedList<TimedPoint> gps = new LinkedList<TimedPoint>();
     104
     105            // Extract dates and locations from GPX input
     106
     107            for (GpxTrack trk : gpxLayer.data.tracks) {
     108                for (Collection<WayPoint> segment : trk.trackSegs) {
     109                    for (WayPoint p : segment) {
     110                    if (!p.attr.containsKey("time"))
     111                        throw new IOException(tr("No time for point {0} x {1}",p.latlon.lat(),p.latlon.lon()));
     112                    Date d = null;
     113                    try {
     114                        d = DateParser.parse((String) p.attr.get("time"));
     115                    } catch (ParseException e) {
     116                        throw new IOException(tr("Cannot read time \"{0}\" from point {1} x {2}",p.attr.get("time"),p.latlon.lat(),p.latlon.lon()));
     117                    }
     118                    gps.add(new TimedPoint(d, p.eastNorth));
     119                }
     120            }
     121            }
     122
     123            if (gps.isEmpty()) {
     124                errorMessage = tr("No images with readable timestamps found.");
     125                return;
     126            }
     127
     128            // read the image files
     129            ArrayList<ImageEntry> data = new ArrayList<ImageEntry>(files.size());
     130            int i = 0;
     131            Main.pleaseWaitDlg.progress.setMaximum(files.size());
     132            for (File f : files) {
     133                if (cancelled)
     134                    break;
     135                Main.pleaseWaitDlg.currentAction.setText(tr("Reading {0}...",f.getName()));
     136                Main.pleaseWaitDlg.progress.setValue(i++);
     137
     138                ImageEntry e = new ImageEntry();
     139                try {
     140                    e.time = ExifReader.readTime(f);
     141                } catch (ParseException e1) {
     142                    continue;
     143                }
     144                if (e.time == null)
     145                    continue;
     146                e.image = f;
     147                e.icon = loadScaledImage(f, 16);
     148
     149                data.add(e);
     150            }
     151            layer = new GeoImageLayer(data, gps);
     152            layer.calculatePosition();
     153        }
     154        @Override protected void finish() {
     155            if (layer != null)
     156                Main.main.addLayer(layer);
     157        }
     158        @Override protected void cancel() {cancelled = true;}
     159    }
     160
     161    public ArrayList<ImageEntry> data;
     162    private LinkedList<TimedPoint> gps = new LinkedList<TimedPoint>();
     163
     164    /**
     165    * The delta added to all timestamps in files from the camera
     166    * to match to the timestamp from the gps receivers tracklog.
     167    */
     168    private long delta = Long.parseLong(Main.pref.get("tagimages.delta", "0"));
     169    private long gpstimezone = Long.parseLong(Main.pref.get("tagimages.gpstimezone", "0"))*60*60*1000;
     170    private boolean mousePressed = false;
     171    private static final SimpleDateFormat dateFormat = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss");
     172    private MouseAdapter mouseAdapter;
     173    private int currentImage;
     174
     175    public static final class GpsTimeIncorrect extends Exception {
     176        public GpsTimeIncorrect(String message, Throwable cause) {
     177            super(message, cause);
     178        }
     179        public GpsTimeIncorrect(String message) {
     180            super(message);
     181        }
     182    }
     183
     184    private static final class TimedPoint implements Comparable<TimedPoint> {
     185        Date time;
     186        EastNorth pos;
     187        public TimedPoint(Date time, EastNorth pos) {
     188            this.time = time;
     189            this.pos = pos;
     190        }
     191        public int compareTo(TimedPoint point) {
     192            return time.compareTo(point.time);
     193        }
     194    }
     195
     196    public static void create(Collection<File> files, GpxLayer gpxLayer) {
     197        Loader loader = new Loader(files, gpxLayer);
     198        Main.worker.execute(loader);
     199    }
     200
     201    private GeoImageLayer(final ArrayList<ImageEntry> data, LinkedList<TimedPoint> gps) {
     202        super(tr("Geotagged Images"));
     203        Collections.sort(data);
     204        Collections.sort(gps);
     205        this.data = data;
     206        this.gps = gps;
     207        mouseAdapter = new MouseAdapter(){
     208            @Override public void mousePressed(MouseEvent e) {
     209                if (e.getButton() != MouseEvent.BUTTON1)
     210                    return;
     211                mousePressed  = true;
     212                if (visible)
     213                    Main.map.mapView.repaint();
     214            }
     215            @Override public void mouseReleased(MouseEvent ev) {
     216                if (ev.getButton() != MouseEvent.BUTTON1)
     217                    return;
     218                mousePressed = false;
     219                if (!visible)
     220                    return;
     221                for (int i = data.size(); i > 0; --i) {
     222                    ImageEntry e = data.get(i-1);
     223                    if (e.pos == null)
     224                        continue;
     225                    Point p = Main.map.mapView.getPoint(e.pos);
     226                    Rectangle r = new Rectangle(p.x-e.icon.getIconWidth()/2, p.y-e.icon.getIconHeight()/2, e.icon.getIconWidth(), e.icon.getIconHeight());
     227                    if (r.contains(ev.getPoint())) {
     228                        showImage(i-1);
     229                        break;
     230                    }
     231                }
     232                Main.map.mapView.repaint();
     233            }
     234        };
     235        Main.map.mapView.addMouseListener(mouseAdapter);
     236        Layer.listeners.add(new LayerChangeListener(){
     237            public void activeLayerChange(Layer oldLayer, Layer newLayer) {}
     238            public void layerAdded(Layer newLayer) {}
     239            public void layerRemoved(Layer oldLayer) {
     240                Main.map.mapView.removeMouseListener(mouseAdapter);
     241            }
     242        });
     243    }
     244
     245    private void showImage(int i) {
     246        currentImage = i;
     247        final JPanel p = new JPanel(new BorderLayout());
     248        final ImageEntry e = data.get(currentImage);
     249        final JScrollPane scroll = new JScrollPane(new JLabel(loadScaledImage(e.image, 580)));
     250        final JViewport vp = scroll.getViewport();
     251        p.add(scroll, BorderLayout.CENTER);
     252
     253        final JToggleButton scale = new JToggleButton(ImageProvider.get("dialogs", "zoom-best-fit"));
     254        final JButton next  = new JButton(ImageProvider.get("dialogs", "next"));
     255        final JButton prev = new JButton(ImageProvider.get("dialogs", "previous"));
     256        final JToggleButton cent = new JToggleButton(ImageProvider.get("dialogs", "centreview"));
     257
     258        JPanel p2 = new JPanel();
     259        p2.add(prev);
     260        p2.add(scale);
     261        p2.add(cent);
     262        p2.add(next);
     263        prev.setEnabled(currentImage>0?true:false);
     264        next.setEnabled(currentImage<data.size()-1?true:false);
     265        p.add(p2, BorderLayout.SOUTH);
     266        final JOptionPane pane = new JOptionPane(p, JOptionPane.PLAIN_MESSAGE);
     267        final JDialog dlg = pane.createDialog(Main.parent, e.image+" ("+e.coor.toDisplayString()+")");
     268        scale.addActionListener(new ActionListener(){
     269            public void actionPerformed(ActionEvent ev) {
     270                p.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
     271                if (scale.getModel().isSelected())
     272                    ((JLabel)vp.getView()).setIcon(loadScaledImage(e.image, Math.max(vp.getWidth(), vp.getHeight())));
     273                else
     274                    ((JLabel)vp.getView()).setIcon(new ImageIcon(e.image.getPath()));
     275                p.setCursor(Cursor.getDefaultCursor());
     276            }
     277        });
     278        scale.setSelected(true);
     279        cent.addActionListener(new ActionListener(){
     280            public void actionPerformed(ActionEvent ev) {
     281                final ImageEntry e = data.get(currentImage);
     282                if (cent.getModel().isSelected())
     283                    Main.map.mapView.zoomTo(e.pos, Main.map.mapView.getScale());
     284            }
     285        });
     286
     287        ActionListener nextprevAction = new ActionListener(){
     288            public void actionPerformed(ActionEvent ev) {
     289                p.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
     290                if (ev.getActionCommand().equals("Next")) {
     291                    currentImage++;
     292                    if(currentImage>=data.size()-1) next.setEnabled(false);
     293                    prev.setEnabled(true);
     294                } else {
     295                    currentImage--;
     296                    if(currentImage<=0) prev.setEnabled(false);
     297                    next.setEnabled(true);
     298                }
     299
     300                final ImageEntry e = data.get(currentImage);
     301                if (scale.getModel().isSelected())
     302                    ((JLabel)vp.getView()).setIcon(loadScaledImage(e.image, Math.max(vp.getWidth(), vp.getHeight())));
     303                else
     304                    ((JLabel)vp.getView()).setIcon(new ImageIcon(e.image.getPath()));
     305                dlg.setTitle(e.image+" ("+e.coor.toDisplayString()+")");
     306                if (cent.getModel().isSelected())
     307                    Main.map.mapView.zoomTo(e.pos, Main.map.mapView.getScale());
     308                p.setCursor(Cursor.getDefaultCursor());
     309            }
     310        };
     311        next.setActionCommand("Next");
     312        prev.setActionCommand("Previous");
     313        next.setMnemonic(KeyEvent.VK_RIGHT);
     314        prev.setMnemonic(KeyEvent.VK_LEFT);
     315        scale.setMnemonic(KeyEvent.VK_F);
     316        cent.setMnemonic(KeyEvent.VK_C);
     317        next.setToolTipText("Show next image");
     318        prev.setToolTipText("Show previous image");
     319        cent.setToolTipText("Centre image location in main display");
     320        scale.setToolTipText("Scale image to fit");
     321
     322        prev.addActionListener(nextprevAction);
     323        next.addActionListener(nextprevAction);
     324        cent.setSelected(false);
     325
     326        dlg.addComponentListener(new ComponentListener() {
     327            boolean ignoreEvent = true;
     328            public void componentHidden(ComponentEvent e) {}
     329            public void componentMoved(ComponentEvent e) {}
     330            public void componentResized(ComponentEvent ev) {
     331                // we ignore the first resize event, as the picture is scaled already on load:
     332                if (scale.getModel().isSelected() && !ignoreEvent) {
     333                    ((JLabel)vp.getView()).setIcon(loadScaledImage(e.image, Math.max(vp.getWidth(), vp.getHeight())));
     334                }
     335                ignoreEvent = false;
     336            }
     337            public void componentShown(ComponentEvent e) {}
     338
     339        });
     340        dlg.setModal(false);
     341        dlg.setVisible(true);
     342        dlg.setResizable(true);
     343    }
     344
     345    @Override public Icon getIcon() {
     346        return ImageProvider.get("layer", "tagimages_small");
     347    }
     348
     349    @Override public Object getInfoComponent() {
     350        JPanel p = new JPanel(new GridBagLayout());
     351        p.add(new JLabel(getToolTipText()), GBC.eop());
     352
     353        p.add(new JLabel(tr("GPS start: {0}",dateFormat.format(gps.getFirst().time))), GBC.eol());
     354        p.add(new JLabel(tr("GPS end: {0}",dateFormat.format(gps.getLast().time))), GBC.eop());
     355
     356        p.add(new JLabel(tr("current delta: {0}s",(delta/1000.0))), GBC.eol());
     357        p.add(new JLabel(tr("timezone difference: ")+(gpstimezone>0?"+":"")+(gpstimezone/1000/60/60)), GBC.eop());
     358
     359        JList img = new JList(data.toArray());
     360        img.setCellRenderer(new DefaultListCellRenderer(){
     361            @Override public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
     362                super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
     363                ImageEntry e = (ImageEntry)value;
     364                setIcon(e.icon);
     365                setText(e.image.getName()+" ("+dateFormat.format(new Date(e.time.getTime()+(delta+gpstimezone)))+")");
     366                if (e.pos == null)
     367                    setForeground(Color.red);
     368                return this;
     369            }
     370        });
     371        img.setVisibleRowCount(5);
     372        p.add(new JScrollPane(img), GBC.eop().fill(GBC.BOTH));
     373        return p;
     374    }
     375
     376    @Override public String getToolTipText() {
     377        int i = 0;
     378        for (ImageEntry e : data)
     379            if (e.pos != null)
     380                i++;
     381        return data.size()+" "+trn("image","images",data.size())+". "+tr("{0} within the track.",i);
     382    }
     383
     384    @Override public boolean isMergable(Layer other) {
     385        return other instanceof GeoImageLayer;
     386    }
     387
     388    @Override public void mergeFrom(Layer from) {
     389        GeoImageLayer l = (GeoImageLayer)from;
     390        data.addAll(l.data);
     391    }
     392
     393    @Override public void paint(Graphics g, MapView mv) {
     394        boolean clickedFound = false;
     395        for (ImageEntry e : data) {
     396            if (e.pos != null) {
     397                Point p = mv.getPoint(e.pos);
     398                Rectangle r = new Rectangle(p.x-e.icon.getIconWidth()/2, p.y-e.icon.getIconHeight()/2, e.icon.getIconWidth(), e.icon.getIconHeight());
     399                e.icon.paintIcon(mv, g, r.x, r.y);
     400                Border b = null;
     401                Point mousePosition = mv.getMousePosition();
     402                if (mousePosition == null)
     403                    continue; // mouse outside the whole window
     404                if (!clickedFound && mousePressed && r.contains(mousePosition)) {
     405                    b = BorderFactory.createBevelBorder(BevelBorder.LOWERED);
     406                    clickedFound = true;
     407                } else
     408                    b = BorderFactory.createBevelBorder(BevelBorder.RAISED);
     409                Insets inset = b.getBorderInsets(mv);
     410                r.grow((inset.top+inset.bottom)/2, (inset.left+inset.right)/2);
     411                b.paintBorder(mv, g, r.x, r.y, r.width, r.height);
     412            }
     413        }
     414    }
     415
     416    @Override public void visitBoundingBox(BoundingXYVisitor v) {
     417        for (ImageEntry e : data)
     418            v.visit(e.pos);
     419    }
     420
     421    @Override public Component[] getMenuEntries() {
     422        JMenuItem sync = new JMenuItem(tr("Sync clock"), ImageProvider.get("clock"));
     423        sync.addActionListener(new ActionListener(){
     424            public void actionPerformed(ActionEvent e) {
     425                JFileChooser fc = new JFileChooser(Main.pref.get("tagimages.lastdirectory"));
     426                fc.setFileSelectionMode(JFileChooser.FILES_ONLY);
     427                fc.setAcceptAllFileFilterUsed(false);
     428                fc.setFileFilter(new FileFilter(){
     429                    @Override public boolean accept(File f) {
     430                        return f.isDirectory() || f.getName().toLowerCase().endsWith(".jpg");
     431                    }
     432                    @Override public String getDescription() {
     433                        return tr("JPEG images (*.jpg)");
     434                    }
     435                });
     436                fc.showOpenDialog(Main.parent);
     437                File sel = fc.getSelectedFile();
     438                if (sel == null)
     439                    return;
     440                Main.pref.put("tagimages.lastdirectory", sel.getPath());
     441                sync(sel);
     442                Main.map.repaint();
     443            }
     444        });
     445        return new Component[]{
     446                new JMenuItem(new LayerListDialog.ShowHideLayerAction(this)),
     447                new JMenuItem(new LayerListDialog.DeleteLayerAction(this)),
     448                new JSeparator(),
     449                sync,
     450                new JSeparator(),
     451                new JMenuItem(new RenameLayerAction(null, this)),
     452                new JSeparator(),
     453                new JMenuItem(new LayerListPopup.InfoAction(this))};
     454    }
     455
     456    private void calculatePosition() {
     457        for (ImageEntry e : data) {
     458            TimedPoint lastTP = null;
     459            for (TimedPoint tp : gps) {
     460                Date time = new Date(tp.time.getTime() - (delta+gpstimezone));
     461                if (time.after(e.time) && lastTP != null) {
     462                    double x = (lastTP.pos.east()+tp.pos.east())/2;
     463                    double y = (lastTP.pos.north()+tp.pos.north())/2;
     464                    e.pos = new EastNorth(x,y);
     465                    break;
     466                }
     467                lastTP = tp;
     468            }
     469            if (e.pos != null)
     470                e.coor = Main.proj.eastNorth2latlon(e.pos);
     471        }
     472    }
     473
     474    private void sync(File f) {
     475        Date exifDate;
     476        try {
     477            exifDate = ExifReader.readTime(f);
     478        } catch (ParseException e) {
     479            JOptionPane.showMessageDialog(Main.parent, tr("The date in file \"{0}\" could not be parsed.", f.getName()));
     480            return;
     481        }
     482        if (exifDate == null) {
     483            JOptionPane.showMessageDialog(Main.parent, tr("There is no EXIF time within the file \"{0}\".", f.getName()));
     484            return;
     485        }
     486        JPanel p = new JPanel(new GridBagLayout());
     487        p.add(new JLabel(tr("Image")), GBC.eol());
     488        p.add(new JLabel(loadScaledImage(f, 300)), GBC.eop());
     489        p.add(new JLabel(tr("Enter shown date (mm/dd/yyyy HH:MM:SS)")), GBC.eol());
     490        JTextField gpsText = new JTextField(dateFormat.format(new Date(exifDate.getTime()+delta)));
     491        p.add(gpsText, GBC.eol().fill(GBC.HORIZONTAL));
     492        p.add(new JLabel(tr("GPS unit timezone (difference to photo)")), GBC.eol());
     493        String t = Main.pref.get("tagimages.gpstimezone", "0");
     494        if (t.charAt(0) != '-')
     495            t = "+"+t;
     496        JTextField gpsTimezone = new JTextField(t);
     497        p.add(gpsTimezone, GBC.eol().fill(GBC.HORIZONTAL));
     498
     499        while (true) {
     500            int answer = JOptionPane.showConfirmDialog(Main.parent, p, tr("Syncronize Time with GPS Unit"), JOptionPane.OK_CANCEL_OPTION);
     501            if (answer != JOptionPane.OK_OPTION || gpsText.getText().equals(""))
     502                return;
     503            try {
     504                delta = DateParser.parse(gpsText.getText()).getTime() - exifDate.getTime();
     505                String time = gpsTimezone.getText();
     506                if (!time.equals("") && time.charAt(0) == '+')
     507                    time = time.substring(1);
     508                if (time.equals(""))
     509                    time = "0";
     510                gpstimezone = Long.valueOf(time)*60*60*1000;
     511                Main.pref.put("tagimages.delta", ""+delta);
     512                Main.pref.put("tagimages.gpstimezone", time);
     513                calculatePosition();
     514                return;
     515            } catch (NumberFormatException x) {
     516                JOptionPane.showMessageDialog(Main.parent, tr("Time entered could not be parsed."));
     517            } catch (ParseException x) {
     518                JOptionPane.showMessageDialog(Main.parent, tr("Time entered could not be parsed."));
     519            }
     520        }
     521    }
     522
     523    private static Icon loadScaledImage(File f, int maxSize) {
     524        Image img = new ImageIcon(f.getPath()).getImage();
     525        int w = img.getWidth(null);
     526        int h = img.getHeight(null);
     527        if (w>h) {
     528            h = Math.round(maxSize*((float)h/w));
     529            w = maxSize;
     530        } else {
     531            w = Math.round(maxSize*((float)w/h));
     532            h = maxSize;
     533        }
     534        return new ImageIcon(createResizedCopy(img, w, h));
     535    }
     536
     537    private static BufferedImage createResizedCopy(Image originalImage,
     538            int scaledWidth, int scaledHeight)
     539    {
     540        BufferedImage scaledBI = new BufferedImage(scaledWidth, scaledHeight, BufferedImage.TYPE_INT_RGB);
     541        Graphics2D g = scaledBI.createGraphics();
     542
     543        g.drawImage(originalImage, 0, 0, scaledWidth, scaledHeight, null);
     544        g.dispose();
     545        return scaledBI;
     546    }
    547547}
  • trunk/src/org/openstreetmap/josm/gui/layer/GpxLayer.java

    r1165 r1169  
    8080
    8181public class GpxLayer extends Layer {
    82         public GpxData data;
    83         private final GpxLayer me;
    84         protected static final double PHI = Math.toRadians(15);
    85         private boolean computeCacheInSync;
    86         private int computeCacheMaxLineLengthUsed;
    87         private Color computeCacheColorUsed;
    88         private boolean computeCacheColored;
    89 
    90         public GpxLayer(GpxData d) {
    91                 super((String) d.attr.get("name"));
    92                 data = d;
    93                 me = this;
    94                 computeCacheInSync = false;
    95         }
    96 
    97         public GpxLayer(GpxData d, String name) {
    98                 this(d);
    99                 this.name = name;
    100         }
    101 
    102         @Override public Icon getIcon() {
    103                 return ImageProvider.get("layer", "gpx_small");
    104         }
    105 
    106         @Override public Object getInfoComponent() {
    107                 return getToolTipText();
    108         }
    109 
    110         @Override public Component[] getMenuEntries() {
    111                 JMenuItem line = new JMenuItem(tr("Customize line drawing"), ImageProvider.get("mapmode/addsegment"));
    112                 line.addActionListener(new ActionListener() {
    113                         public void actionPerformed(ActionEvent e) {
    114                                 JRadioButton[] r = new JRadioButton[3];
    115                                 r[0] = new JRadioButton(tr("Use global settings."));
    116                                 r[1] = new JRadioButton(tr("Draw lines between points for this layer."));
    117                                 r[2] = new JRadioButton(tr("Do not draw lines between points for this layer."));
    118                                 ButtonGroup group = new ButtonGroup();
    119                                 Box panel = Box.createVerticalBox();
    120                                 for (JRadioButton b : r) {
    121                                         group.add(b);
    122                                         panel.add(b);
    123                                 }
    124                                 String propName = "draw.rawgps.lines.layer "+name;
    125                                 if (Main.pref.hasKey(propName))
    126                                         group.setSelected(r[Main.pref.getBoolean(propName) ? 1:2].getModel(), true);
    127                                 else
    128                                         group.setSelected(r[0].getModel(), true);
    129                                 int answer = JOptionPane.showConfirmDialog(Main.parent, panel, tr("Select line drawing options"), JOptionPane.OK_CANCEL_OPTION);
    130                                 if (answer == JOptionPane.CANCEL_OPTION)
    131                                         return;
    132                                 if (group.getSelection() == r[0].getModel())
    133                                         Main.pref.put(propName, null);
    134                                 else
    135                                         Main.pref.put(propName, group.getSelection() == r[1].getModel());
    136                                 Main.map.repaint();
    137                         }
    138                 });
    139 
    140                 JMenuItem color = new JMenuItem(tr("Customize Color"), ImageProvider.get("colorchooser"));
    141                 color.putClientProperty("help", "Action/LayerCustomizeColor");
    142                 color.addActionListener(new ActionListener() {
    143                         public void actionPerformed(ActionEvent e) {
    144                                 JColorChooser c = new JColorChooser(Main.pref.getColor(marktr("gps point"), "layer "+name, Color.gray));
    145                                 Object[] options = new Object[]{tr("OK"), tr("Cancel"), tr("Default")};
    146                                 int answer = JOptionPane.showOptionDialog(Main.parent, c, tr("Choose a color"), JOptionPane.OK_CANCEL_OPTION,
    147                                 JOptionPane.PLAIN_MESSAGE, null, options, options[0]);
    148                                 switch (answer) {
    149                                 case 0:
    150                                         Main.pref.putColor("layer "+name, c.getColor());
    151                                         break;
    152                                 case 1:
    153                                         return;
    154                                 case 2:
    155                                         Main.pref.putColor("layer "+name, null);
    156                                         break;
    157                                 }
    158                                 Main.map.repaint();
    159                         }
    160                 });
    161 
    162                 JMenuItem markersFromNamedTrackpoints = new JMenuItem(tr("Markers From Named Points"), ImageProvider.get("addmarkers"));
    163                 markersFromNamedTrackpoints.putClientProperty("help", "Action/MarkersFromNamedPoints");
    164                 markersFromNamedTrackpoints.addActionListener(new ActionListener() {
    165                         public void actionPerformed(ActionEvent e) {
    166                                 GpxData namedTrackPoints = new GpxData();
    167                                 for (GpxTrack track : data.tracks)
    168                                         for (Collection<WayPoint> seg : track.trackSegs)
    169                                                 for (WayPoint point : seg)
    170                                                         if (point.attr.containsKey("name") || point.attr.containsKey("desc"))
    171                                                                 namedTrackPoints.waypoints.add(point);
    172 
    173                                 MarkerLayer ml = new MarkerLayer(namedTrackPoints, tr("Named Trackpoints from {0}", name), associatedFile, me);
    174                                 if (ml.data.size() > 0) {
    175                                         Main.main.addLayer(ml);
    176                                 }
    177                         }
    178                 });
    179 
    180                 JMenuItem importAudio = new JMenuItem(tr("Import Audio"), ImageProvider.get("importaudio"));
    181                 importAudio.putClientProperty("help", "ImportAudio");
    182                 importAudio.addActionListener(new ActionListener() {
    183                         public void actionPerformed(ActionEvent e) {
    184                                 String dir = Main.pref.get("markers.lastaudiodirectory");
    185                                 JFileChooser fc = new JFileChooser(dir);
    186                                 fc.setFileSelectionMode(JFileChooser.FILES_ONLY);
    187                                 fc.setAcceptAllFileFilterUsed(false);
    188                                 fc.setFileFilter(new FileFilter(){
    189                                         @Override public boolean accept(File f) {
    190                                                 return f.isDirectory() || f.getName().toLowerCase().endsWith(".wav");
    191                                         }
    192                                         @Override public String getDescription() {
    193                                                 return tr("Wave Audio files (*.wav)");
    194                                         }
    195                                 });
    196                                 fc.setMultiSelectionEnabled(true);
    197                                 if(fc.showOpenDialog(Main.parent) == JFileChooser.APPROVE_OPTION) {
    198                                         if (!fc.getCurrentDirectory().getAbsolutePath().equals(dir))
    199                                                 Main.pref.put("markers.lastaudiodirectory", fc.getCurrentDirectory().getAbsolutePath());
    200 
    201                                         // FIXME: properly support multi-selection here.
    202                                         // Calling importAudio several times just creates N maker layers, which
    203                                         // is sub-optimal.
    204                                         File sel[] = fc.getSelectedFiles();
    205                                         if(sel != null)
    206                                                 for (int i = 0; i < sel.length; i++)
    207                                                         importAudio(sel[i]);
    208 
    209                                         Main.map.repaint();
    210                                 }
    211                         }
    212                 });
    213 
    214                 JMenuItem tagimage = new JMenuItem(tr("Import images"), ImageProvider.get("tagimages"));
    215                 tagimage.putClientProperty("help", "Action/ImportImages");
    216                 tagimage.addActionListener(new ActionListener() {
    217                         public void actionPerformed(ActionEvent e) {
    218                                 JFileChooser fc = new JFileChooser(Main.pref.get("tagimages.lastdirectory"));
    219                                 fc.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
    220                                 fc.setMultiSelectionEnabled(true);
    221                                 fc.setAcceptAllFileFilterUsed(false);
    222                                 fc.setFileFilter(new FileFilter() {
    223                                         @Override public boolean accept(File f) {
    224                                                 return f.isDirectory() || f.getName().toLowerCase().endsWith(".jpg");
    225                                         }
    226                                         @Override public String getDescription() {
    227                                                 return tr("JPEG images (*.jpg)");
    228                                         }
    229                                 });
    230                                 fc.showOpenDialog(Main.parent);
    231                                 File[] sel = fc.getSelectedFiles();
    232                                 if (sel == null || sel.length == 0)
    233                                         return;
    234                                 LinkedList<File> files = new LinkedList<File>();
    235                                 addRecursiveFiles(files, sel);
    236                                 Main.pref.put("tagimages.lastdirectory", fc.getCurrentDirectory().getPath());
    237                                 GeoImageLayer.create(files, GpxLayer.this);
    238                         }
    239 
    240                         private void addRecursiveFiles(LinkedList<File> files, File[] sel) {
    241                                 for (File f : sel) {
    242                                         if (f.isDirectory())
    243                                                 addRecursiveFiles(files, f.listFiles());
    244                                         else if (f.getName().toLowerCase().endsWith(".jpg"))
    245                                                 files.add(f);
    246                                 }
    247                         }
    248                 });
    249 
    250                 if (Main.applet)
    251                         return new Component[] {
    252                                 new JMenuItem(new LayerListDialog.ShowHideLayerAction(this)),
    253                                 new JMenuItem(new LayerListDialog.DeleteLayerAction(this)),
    254                                 new JSeparator(),
    255                                 color,
    256                                 line,
    257                                 new JMenuItem(new ConvertToDataLayerAction()),
    258                                 new JSeparator(),
    259                                 new JMenuItem(new RenameLayerAction(associatedFile, this)),
    260                                 new JSeparator(),
    261                                 new JMenuItem(new LayerListPopup.InfoAction(this))};
    262                 return new Component[] {
    263                         new JMenuItem(new LayerListDialog.ShowHideLayerAction(this)),
    264                         new JMenuItem(new LayerListDialog.DeleteLayerAction(this)),
    265                         new JSeparator(),
    266                         new JMenuItem(new SaveAction(this)),
    267                         new JMenuItem(new SaveAsAction(this)),
    268                         // new JMenuItem(new UploadTraceAction()),
    269                         color,
    270                         line,
    271                         tagimage,
    272                         importAudio,
    273                         markersFromNamedTrackpoints,
    274                         new JMenuItem(new ConvertToDataLayerAction()),
     82    public GpxData data;
     83    private final GpxLayer me;
     84    protected static final double PHI = Math.toRadians(15);
     85    private boolean computeCacheInSync;
     86    private int computeCacheMaxLineLengthUsed;
     87    private Color computeCacheColorUsed;
     88    private boolean computeCacheColored;
     89
     90    public GpxLayer(GpxData d) {
     91        super((String) d.attr.get("name"));
     92        data = d;
     93        me = this;
     94        computeCacheInSync = false;
     95    }
     96
     97    public GpxLayer(GpxData d, String name) {
     98        this(d);
     99        this.name = name;
     100    }
     101
     102    @Override public Icon getIcon() {
     103        return ImageProvider.get("layer", "gpx_small");
     104    }
     105
     106    @Override public Object getInfoComponent() {
     107        return getToolTipText();
     108    }
     109
     110    @Override public Component[] getMenuEntries() {
     111        JMenuItem line = new JMenuItem(tr("Customize line drawing"), ImageProvider.get("mapmode/addsegment"));
     112        line.addActionListener(new ActionListener() {
     113            public void actionPerformed(ActionEvent e) {
     114                JRadioButton[] r = new JRadioButton[3];
     115                r[0] = new JRadioButton(tr("Use global settings."));
     116                r[1] = new JRadioButton(tr("Draw lines between points for this layer."));
     117                r[2] = new JRadioButton(tr("Do not draw lines between points for this layer."));
     118                ButtonGroup group = new ButtonGroup();
     119                Box panel = Box.createVerticalBox();
     120                for (JRadioButton b : r) {
     121                    group.add(b);
     122                    panel.add(b);
     123                }
     124                String propName = "draw.rawgps.lines.layer "+name;
     125                if (Main.pref.hasKey(propName))
     126                    group.setSelected(r[Main.pref.getBoolean(propName) ? 1:2].getModel(), true);
     127                else
     128                    group.setSelected(r[0].getModel(), true);
     129                int answer = JOptionPane.showConfirmDialog(Main.parent, panel, tr("Select line drawing options"), JOptionPane.OK_CANCEL_OPTION);
     130                if (answer == JOptionPane.CANCEL_OPTION)
     131                    return;
     132                if (group.getSelection() == r[0].getModel())
     133                    Main.pref.put(propName, null);
     134                else
     135                    Main.pref.put(propName, group.getSelection() == r[1].getModel());
     136                Main.map.repaint();
     137            }
     138        });
     139
     140        JMenuItem color = new JMenuItem(tr("Customize Color"), ImageProvider.get("colorchooser"));
     141        color.putClientProperty("help", "Action/LayerCustomizeColor");
     142        color.addActionListener(new ActionListener() {
     143            public void actionPerformed(ActionEvent e) {
     144                JColorChooser c = new JColorChooser(Main.pref.getColor(marktr("gps point"), "layer "+name, Color.gray));
     145                Object[] options = new Object[]{tr("OK"), tr("Cancel"), tr("Default")};
     146                int answer = JOptionPane.showOptionDialog(Main.parent, c, tr("Choose a color"), JOptionPane.OK_CANCEL_OPTION,
     147                JOptionPane.PLAIN_MESSAGE, null, options, options[0]);
     148                switch (answer) {
     149                case 0:
     150                    Main.pref.putColor("layer "+name, c.getColor());
     151                    break;
     152                case 1:
     153                    return;
     154                case 2:
     155                    Main.pref.putColor("layer "+name, null);
     156                    break;
     157                }
     158                Main.map.repaint();
     159            }
     160        });
     161
     162        JMenuItem markersFromNamedTrackpoints = new JMenuItem(tr("Markers From Named Points"), ImageProvider.get("addmarkers"));
     163        markersFromNamedTrackpoints.putClientProperty("help", "Action/MarkersFromNamedPoints");
     164        markersFromNamedTrackpoints.addActionListener(new ActionListener() {
     165            public void actionPerformed(ActionEvent e) {
     166                GpxData namedTrackPoints = new GpxData();
     167                for (GpxTrack track : data.tracks)
     168                    for (Collection<WayPoint> seg : track.trackSegs)
     169                        for (WayPoint point : seg)
     170                            if (point.attr.containsKey("name") || point.attr.containsKey("desc"))
     171                                namedTrackPoints.waypoints.add(point);
     172
     173                MarkerLayer ml = new MarkerLayer(namedTrackPoints, tr("Named Trackpoints from {0}", name), associatedFile, me);
     174                if (ml.data.size() > 0) {
     175                    Main.main.addLayer(ml);
     176                }
     177            }
     178        });
     179
     180        JMenuItem importAudio = new JMenuItem(tr("Import Audio"), ImageProvider.get("importaudio"));
     181        importAudio.putClientProperty("help", "ImportAudio");
     182        importAudio.addActionListener(new ActionListener() {
     183            public void actionPerformed(ActionEvent e) {
     184                String dir = Main.pref.get("markers.lastaudiodirectory");
     185                JFileChooser fc = new JFileChooser(dir);
     186                fc.setFileSelectionMode(JFileChooser.FILES_ONLY);
     187                fc.setAcceptAllFileFilterUsed(false);
     188                fc.setFileFilter(new FileFilter(){
     189                    @Override public boolean accept(File f) {
     190                        return f.isDirectory() || f.getName().toLowerCase().endsWith(".wav");
     191                    }
     192                    @Override public String getDescription() {
     193                        return tr("Wave Audio files (*.wav)");
     194                    }
     195                });
     196                fc.setMultiSelectionEnabled(true);
     197                if(fc.showOpenDialog(Main.parent) == JFileChooser.APPROVE_OPTION) {
     198                    if (!fc.getCurrentDirectory().getAbsolutePath().equals(dir))
     199                        Main.pref.put("markers.lastaudiodirectory", fc.getCurrentDirectory().getAbsolutePath());
     200
     201                    // FIXME: properly support multi-selection here.
     202                    // Calling importAudio several times just creates N maker layers, which
     203                    // is sub-optimal.
     204                    File sel[] = fc.getSelectedFiles();
     205                    if(sel != null)
     206                        for (int i = 0; i < sel.length; i++)
     207                            importAudio(sel[i]);
     208
     209                    Main.map.repaint();
     210                }
     211            }
     212        });
     213
     214        JMenuItem tagimage = new JMenuItem(tr("Import images"), ImageProvider.get("tagimages"));
     215        tagimage.putClientProperty("help", "Action/ImportImages");
     216        tagimage.addActionListener(new ActionListener() {
     217            public void actionPerformed(ActionEvent e) {
     218                JFileChooser fc = new JFileChooser(Main.pref.get("tagimages.lastdirectory"));
     219                fc.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
     220                fc.setMultiSelectionEnabled(true);
     221                fc.setAcceptAllFileFilterUsed(false);
     222                fc.setFileFilter(new FileFilter() {
     223                    @Override public boolean accept(File f) {
     224                        return f.isDirectory() || f.getName().toLowerCase().endsWith(".jpg");
     225                    }
     226                    @Override public String getDescription() {
     227                        return tr("JPEG images (*.jpg)");
     228                    }
     229                });
     230                fc.showOpenDialog(Main.parent);
     231                File[] sel = fc.getSelectedFiles();
     232                if (sel == null || sel.length == 0)
     233                    return;
     234                LinkedList<File> files = new LinkedList<File>();
     235                addRecursiveFiles(files, sel);
     236                Main.pref.put("tagimages.lastdirectory", fc.getCurrentDirectory().getPath());
     237                GeoImageLayer.create(files, GpxLayer.this);
     238            }
     239
     240            private void addRecursiveFiles(LinkedList<File> files, File[] sel) {
     241                for (File f : sel) {
     242                    if (f.isDirectory())
     243                        addRecursiveFiles(files, f.listFiles());
     244                    else if (f.getName().toLowerCase().endsWith(".jpg"))
     245                        files.add(f);
     246                }
     247            }
     248        });
     249
     250        if (Main.applet)
     251            return new Component[] {
     252                new JMenuItem(new LayerListDialog.ShowHideLayerAction(this)),
     253                new JMenuItem(new LayerListDialog.DeleteLayerAction(this)),
     254                new JSeparator(),
     255                color,
     256                line,
     257                new JMenuItem(new ConvertToDataLayerAction()),
     258                new JSeparator(),
     259                new JMenuItem(new RenameLayerAction(associatedFile, this)),
     260                new JSeparator(),
     261                new JMenuItem(new LayerListPopup.InfoAction(this))};
     262        return new Component[] {
     263            new JMenuItem(new LayerListDialog.ShowHideLayerAction(this)),
     264            new JMenuItem(new LayerListDialog.DeleteLayerAction(this)),
     265            new JSeparator(),
     266            new JMenuItem(new SaveAction(this)),
     267            new JMenuItem(new SaveAsAction(this)),
     268            // new JMenuItem(new UploadTraceAction()),
     269            color,
     270            line,
     271            tagimage,
     272            importAudio,
     273            markersFromNamedTrackpoints,
     274            new JMenuItem(new ConvertToDataLayerAction()),
    275275            new JMenuItem(new DownloadAlongTrackAction()),
    276                         new JSeparator(),
    277                         new JMenuItem(new RenameLayerAction(associatedFile, this)),
    278                         new JSeparator(),
    279                         new JMenuItem(new LayerListPopup.InfoAction(this))};
    280         }
    281 
    282         @Override public String getToolTipText() {
    283                 StringBuilder info = new StringBuilder().append("<html>");
    284 
    285                 info.append(trn("{0} track, ", "{0} tracks, ",
    286                 data.tracks.size(), data.tracks.size())).append(trn("{0} route, ", "{0} routes, ",
    287                 data.routes.size(), data.routes.size())).append(trn("{0} waypoint", "{0} waypoints",
    288                 data.waypoints.size(), data.waypoints.size())).append("<br>");
    289 
    290                 if (data.attr.containsKey("name"))
    291                         info.append(tr("Name: {0}", data.attr.get("name"))).append("<br>");
    292 
    293                 if (data.attr.containsKey("desc"))
    294                         info.append(tr("Description: {0}", data.attr.get("desc"))).append("<br>");
    295 
    296                 if(data.tracks.size() > 0){
    297                         boolean first = true;
    298                         WayPoint earliest = null, latest = null;
    299 
    300                         for(GpxTrack trk: data.tracks){
    301                                 for(Collection<WayPoint> seg:trk.trackSegs){
    302                                         for(WayPoint pnt:seg){
    303                                                 if(first){
    304                                                         latest = earliest = pnt;
    305                                                         first = false;
    306                                                 }else{
    307                                                         if(pnt.compareTo(earliest) < 0){
    308                                                                 earliest = pnt;
    309                                                         }else{
    310                                                                 latest = pnt;
    311                                                         }
    312                                                 }
    313                                         }
    314                                 }
    315                         }
    316                         if (earliest != null && latest != null) {
    317                                 DateFormat df = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.SHORT);
    318                                 info.append(tr("Timespan: ") + df.format(new Date((long)(earliest.time * 1000))) + " - "
    319                                 + df.format(new Date((long)(latest.time * 1000))));
    320                                 int diff = (int)(latest.time - earliest.time);
    321                                 info.append(" (" + (diff / 3600) + ":" + ((diff % 3600)/60) + ")");
    322                                 info.append("<br>");
    323                         }
    324                 }
    325                 info.append(tr("Length: ") + new DecimalFormat("#0.00").format(data.length() / 1000) + "km");
    326                 info.append("<br>");
    327 
    328                 return info.append("</html>").toString();
    329         }
    330 
    331         @Override public boolean isMergable(Layer other) {
    332                 return other instanceof GpxLayer;
    333         }
    334 
    335         @Override public void mergeFrom(Layer from) {
    336                 data.mergeFrom(((GpxLayer)from).data);
    337                 computeCacheInSync = false;
    338         }
    339 
    340         private static Color[] colors = new Color[256];
    341         static {
    342                 for (int i = 0; i < colors.length; i++) {
    343                         colors[i] = Color.getHSBColor(i/300.0f, 1, 1);
    344                 }
    345         }
    346 
    347         // lookup array to draw arrows without doing any math
    348         private static int ll0 = 9;
    349         private static int sl4 = 5;
    350         private static int sl9 = 3;
    351         private static int[][] dir = {
    352                 {+sl4,+ll0,+ll0,+sl4},
    353                 {-sl9,+ll0,+sl9,+ll0},
    354                 {-ll0,+sl4,-sl4,+ll0},
    355                 {-ll0,-sl9,-ll0,+sl9},
    356                 {-sl4,-ll0,-ll0,-sl4},
    357                 {+sl9,-ll0,-sl9,-ll0},
    358                 {+ll0,-sl4,+sl4,-ll0},
    359                 {+ll0,+sl9,+ll0,-sl9},
    360                 {+sl4,+ll0,+ll0,+sl4},
    361                 {-sl9,+ll0,+sl9,+ll0},
    362                 {-ll0,+sl4,-sl4,+ll0},
    363                 {-ll0,-sl9,-ll0,+sl9}
    364         };
    365 
    366         @Override public void paint(Graphics g, MapView mv) {
    367 
    368                 /****************************************************************
    369                 ********** STEP 1 - GET CONFIG VALUES **************************
    370                 ****************************************************************/
    371                 // Long startTime = System.currentTimeMillis();
    372                 Color neutralColor = Main.pref.getColor(marktr("gps point"), "layer "+name, Color.GRAY);
    373                 boolean forceLines = Main.pref.getBoolean("draw.rawgps.lines.force");                     // also draw lines between points belonging to different segments
    374                 boolean direction = Main.pref.getBoolean("draw.rawgps.direction");                        // draw direction arrows on the lines
    375                 int maxLineLength = -1;
    376                 try {
    377                         maxLineLength = Integer.parseInt(Main.pref.get("draw.rawgps.max-line-length", "-1"));   // don't draw lines if longer than x meters
    378                 } catch (java.lang.NumberFormatException e) {
    379                         Main.pref.put("draw.rawgps.max-line-length", "-1");
    380                 }
    381                 boolean lines = Main.pref.getBoolean("draw.rawgps.lines");                                // draw line between points, global setting
    382                 String linesKey = "draw.rawgps.lines.layer "+name;
    383                 if (Main.pref.hasKey(linesKey))
    384                         lines = Main.pref.getBoolean(linesKey);                                                 // draw lines, per-layer setting
    385                 boolean large = Main.pref.getBoolean("draw.rawgps.large");                                // paint large dots for points
    386                 boolean colored = Main.pref.getBoolean("draw.rawgps.colors");                             // color the lines
    387                 boolean alternatedirection = Main.pref.getBoolean("draw.rawgps.alternatedirection");      // paint direction arrow with alternate math. may be faster
    388                 int delta = 0;
    389                 try {
    390                         delta = Integer.parseInt(Main.pref.get("draw.rawgps.min-arrow-distance", "0"));         // don't draw arrows nearer to each other than this
    391                 } catch (java.lang.NumberFormatException e) {
    392                         Main.pref.put("draw.rawgps.min-arrow-distance", "0");
    393                 }
    394 
    395                 /****************************************************************
    396                 ********** STEP 2a - CHECK CACHE VALIDITY **********************
    397                 ****************************************************************/
    398                 if (computeCacheInSync && ((computeCacheMaxLineLengthUsed != maxLineLength) ||
    399                                                                    (!neutralColor.equals(computeCacheColorUsed)) ||
    400                                                                    (computeCacheColored != colored))) {
     276            new JSeparator(),
     277            new JMenuItem(new RenameLayerAction(associatedFile, this)),
     278            new JSeparator(),
     279            new JMenuItem(new LayerListPopup.InfoAction(this))};
     280    }
     281
     282    @Override public String getToolTipText() {
     283        StringBuilder info = new StringBuilder().append("<html>");
     284
     285        info.append(trn("{0} track, ", "{0} tracks, ",
     286        data.tracks.size(), data.tracks.size())).append(trn("{0} route, ", "{0} routes, ",
     287        data.routes.size(), data.routes.size())).append(trn("{0} waypoint", "{0} waypoints",
     288        data.waypoints.size(), data.waypoints.size())).append("<br>");
     289
     290        if (data.attr.containsKey("name"))
     291            info.append(tr("Name: {0}", data.attr.get("name"))).append("<br>");
     292
     293        if (data.attr.containsKey("desc"))
     294            info.append(tr("Description: {0}", data.attr.get("desc"))).append("<br>");
     295
     296        if(data.tracks.size() > 0){
     297            boolean first = true;
     298            WayPoint earliest = null, latest = null;
     299
     300            for(GpxTrack trk: data.tracks){
     301                for(Collection<WayPoint> seg:trk.trackSegs){
     302                    for(WayPoint pnt:seg){
     303                        if(first){
     304                            latest = earliest = pnt;
     305                            first = false;
     306                        }else{
     307                            if(pnt.compareTo(earliest) < 0){
     308                                earliest = pnt;
     309                            }else{
     310                                latest = pnt;
     311                            }
     312                        }
     313                    }
     314                }
     315            }
     316            if (earliest != null && latest != null) {
     317                DateFormat df = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.SHORT);
     318                info.append(tr("Timespan: ") + df.format(new Date((long)(earliest.time * 1000))) + " - "
     319                + df.format(new Date((long)(latest.time * 1000))));
     320                int diff = (int)(latest.time - earliest.time);
     321                info.append(" (" + (diff / 3600) + ":" + ((diff % 3600)/60) + ")");
     322                info.append("<br>");
     323            }
     324        }
     325        info.append(tr("Length: ") + new DecimalFormat("#0.00").format(data.length() / 1000) + "km");
     326        info.append("<br>");
     327
     328        return info.append("</html>").toString();
     329    }
     330
     331    @Override public boolean isMergable(Layer other) {
     332        return other instanceof GpxLayer;
     333    }
     334
     335    @Override public void mergeFrom(Layer from) {
     336        data.mergeFrom(((GpxLayer)from).data);
     337        computeCacheInSync = false;
     338    }
     339
     340    private static Color[] colors = new Color[256];
     341    static {
     342        for (int i = 0; i < colors.length; i++) {
     343            colors[i] = Color.getHSBColor(i/300.0f, 1, 1);
     344        }
     345    }
     346
     347    // lookup array to draw arrows without doing any math
     348    private static int ll0 = 9;
     349    private static int sl4 = 5;
     350    private static int sl9 = 3;
     351    private static int[][] dir = {
     352        {+sl4,+ll0,+ll0,+sl4},
     353        {-sl9,+ll0,+sl9,+ll0},
     354        {-ll0,+sl4,-sl4,+ll0},
     355        {-ll0,-sl9,-ll0,+sl9},
     356        {-sl4,-ll0,-ll0,-sl4},
     357        {+sl9,-ll0,-sl9,-ll0},
     358        {+ll0,-sl4,+sl4,-ll0},
     359        {+ll0,+sl9,+ll0,-sl9},
     360        {+sl4,+ll0,+ll0,+sl4},
     361        {-sl9,+ll0,+sl9,+ll0},
     362        {-ll0,+sl4,-sl4,+ll0},
     363        {-ll0,-sl9,-ll0,+sl9}
     364    };
     365
     366    @Override public void paint(Graphics g, MapView mv) {
     367
     368        /****************************************************************
     369        ********** STEP 1 - GET CONFIG VALUES **************************
     370        ****************************************************************/
     371        // Long startTime = System.currentTimeMillis();
     372        Color neutralColor = Main.pref.getColor(marktr("gps point"), "layer "+name, Color.GRAY);
     373        boolean forceLines = Main.pref.getBoolean("draw.rawgps.lines.force");                     // also draw lines between points belonging to different segments
     374        boolean direction = Main.pref.getBoolean("draw.rawgps.direction");                        // draw direction arrows on the lines
     375        int maxLineLength = -1;
     376        try {
     377            maxLineLength = Integer.parseInt(Main.pref.get("draw.rawgps.max-line-length", "-1"));   // don't draw lines if longer than x meters
     378        } catch (java.lang.NumberFormatException e) {
     379            Main.pref.put("draw.rawgps.max-line-length", "-1");
     380        }
     381        boolean lines = Main.pref.getBoolean("draw.rawgps.lines");                                // draw line between points, global setting
     382        String linesKey = "draw.rawgps.lines.layer "+name;
     383        if (Main.pref.hasKey(linesKey))
     384            lines = Main.pref.getBoolean(linesKey);                                                 // draw lines, per-layer setting
     385        boolean large = Main.pref.getBoolean("draw.rawgps.large");                                // paint large dots for points
     386        boolean colored = Main.pref.getBoolean("draw.rawgps.colors");                             // color the lines
     387        boolean alternatedirection = Main.pref.getBoolean("draw.rawgps.alternatedirection");      // paint direction arrow with alternate math. may be faster
     388        int delta = 0;
     389        try {
     390            delta = Integer.parseInt(Main.pref.get("draw.rawgps.min-arrow-distance", "0"));         // don't draw arrows nearer to each other than this
     391        } catch (java.lang.NumberFormatException e) {
     392            Main.pref.put("draw.rawgps.min-arrow-distance", "0");
     393        }
     394
     395        /****************************************************************
     396        ********** STEP 2a - CHECK CACHE VALIDITY **********************
     397        ****************************************************************/
     398        if (computeCacheInSync && ((computeCacheMaxLineLengthUsed != maxLineLength) ||
     399                                   (!neutralColor.equals(computeCacheColorUsed)) ||
     400                                   (computeCacheColored != colored))) {
    401401//          System.out.println("(re-)computing gpx line styles, reason: CCIS=" + computeCacheInSync + " CCMLLU=" + (computeCacheMaxLineLengthUsed != maxLineLength) + " CCCU=" +  (!neutralColor.equals(computeCacheColorUsed)) + " CCC=" + (computeCacheColored != colored));
    402                         computeCacheMaxLineLengthUsed = maxLineLength;
    403                         computeCacheInSync = false;
    404                         computeCacheColorUsed = neutralColor;
    405                         computeCacheColored = colored;
    406                 }
    407 
    408                 /****************************************************************
    409                 ********** STEP 2b - RE-COMPUTE CACHE DATA *********************
    410                 ****************************************************************/
    411                 if (!computeCacheInSync) { // don't compute if the cache is good
    412                         WayPoint oldWp = null;
    413                         for (GpxTrack trk : data.tracks) {
    414                                 if (!forceLines) { // don't draw lines between segments, unless forced to
    415                                         oldWp = null;
    416                                 }
    417                                 for (Collection<WayPoint> segment : trk.trackSegs) {
    418                                         for (WayPoint trkPnt : segment) {
    419                                                 if (Double.isNaN(trkPnt.latlon.lat()) || Double.isNaN(trkPnt.latlon.lon())) {
    420                                                         continue;
    421                                                 }
    422                                                 if (oldWp != null) {
    423                                                         double dist = trkPnt.latlon.greatCircleDistance(oldWp.latlon);
    424                                                         double dtime = trkPnt.time - oldWp.time;
    425                                                         double vel = dist/dtime;
    426 
    427                                                         if (!colored) {
    428                                                                 trkPnt.speedLineColor = neutralColor;
    429                                                         } else if (dtime <= 0 || vel < 0 || vel > 36) { // attn: bad case first
    430                                                                 trkPnt.speedLineColor = colors[255];
    431                                                         } else {
    432                                                                 trkPnt.speedLineColor = colors[(int) (7*vel)];
    433                                                         }
    434                                                         if (maxLineLength == -1 || dist <= maxLineLength) {
    435                                                                 trkPnt.drawLine = true;
    436                                                                 trkPnt.dir = (int)(Math.atan2(-trkPnt.eastNorth.north()+oldWp.eastNorth.north(), trkPnt.eastNorth.east()-oldWp.eastNorth.east()) / Math.PI * 4 + 3.5); // crude but works
    437                                                         } else {
    438                                                                 trkPnt.drawLine = false;
    439                                                         }
    440                                                 } else { // make sure we reset outdated data
    441                                                         trkPnt.speedLineColor = colors[255];
    442                                                         trkPnt.drawLine = false;
    443                                                 }
    444                                                 oldWp = trkPnt;
    445                                         }
    446                                 }
    447                         }
    448                         computeCacheInSync = true;
    449                 }
    450 
    451                 /****************************************************************
    452                 ********** STEP 3a - DRAW LINES ********************************
    453                 ****************************************************************/
    454                 if (lines) {
    455                 Point old = null;
    456                 for (GpxTrack trk : data.tracks) {
    457                         for (Collection<WayPoint> segment : trk.trackSegs) {
    458                                 for (WayPoint trkPnt : segment) {
    459                                         if (Double.isNaN(trkPnt.latlon.lat()) || Double.isNaN(trkPnt.latlon.lon()))
    460                                                 continue;
    461                                         Point screen = mv.getPoint(trkPnt.eastNorth);
    462                                                 if (trkPnt.drawLine) {
    463                                                         // skip points that are on the same screenposition
    464                                                         if (old != null && ((old.x != screen.x) || (old.y != screen.y))) {
    465                                                                 g.setColor(trkPnt.speedLineColor);
    466                                                                 g.drawLine(old.x, old.y, screen.x, screen.y);
    467                                                         }
    468                                                 }
    469                                                 old = screen;
    470                                         } // end for trkpnt
    471                                 } // end for segment
    472                         } // end for trk
    473                 } // end if lines
    474 
    475                 /****************************************************************
    476                 ********** STEP 3b - DRAW NICE ARROWS **************************
    477                 ****************************************************************/
    478                 if (lines && direction && !alternatedirection) {
    479                         Point old = null;
    480                         Point oldA = null; // last arrow painted
    481                         for (GpxTrack trk : data.tracks) {
    482                                 for (Collection<WayPoint> segment : trk.trackSegs) {
    483                                         for (WayPoint trkPnt : segment) {
    484                                                 if (Double.isNaN(trkPnt.latlon.lat()) || Double.isNaN(trkPnt.latlon.lon()))
    485                                                         continue;
    486                                                 if (trkPnt.drawLine) {
    487                                                         Point screen = mv.getPoint(trkPnt.eastNorth);
    488                                                         // skip points that are on the same screenposition
    489                                                         if (old != null && (oldA == null || screen.x < oldA.x-delta || screen.x > oldA.x+delta || screen.y < oldA.y-delta || screen.y > oldA.y+delta)) {
    490                                                                 g.setColor(trkPnt.speedLineColor);
    491                                                                 double t = Math.atan2(screen.y-old.y, screen.x-old.x) + Math.PI;
    492                                                                 g.drawLine(screen.x,screen.y, (int)(screen.x + 10*Math.cos(t-PHI)), (int)(screen.y
    493                                                                 + 10*Math.sin(t-PHI)));
    494                                                                 g.drawLine(screen.x,screen.y, (int)(screen.x + 10*Math.cos(t+PHI)), (int)(screen.y
    495                                                                 + 10*Math.sin(t+PHI)));
    496                                                                 oldA = screen;
    497                                                         }
    498                                                         old = screen;
    499                                                 }
    500                                         } // end for trkpnt
    501                                 } // end for segment
    502                         } // end for trk
    503                 } // end if lines
    504 
    505                 /****************************************************************
    506                 ********** STEP 3c - DRAW FAST ARROWS **************************
    507                 ****************************************************************/
    508                 if (lines && direction && alternatedirection) {
    509                         Point old = null;
    510                         Point oldA = null; // last arrow painted
    511                         for (GpxTrack trk : data.tracks) {
    512                                 for (Collection<WayPoint> segment : trk.trackSegs) {
    513                                         for (WayPoint trkPnt : segment) {
    514                                                 if (Double.isNaN(trkPnt.latlon.lat()) || Double.isNaN(trkPnt.latlon.lon()))
    515                                                         continue;
    516                                                 if (trkPnt.drawLine) {
    517                                                         Point screen = mv.getPoint(trkPnt.eastNorth);
    518                                                         // skip points that are on the same screenposition
    519                                                         if (old != null && (oldA == null || screen.x < oldA.x-delta || screen.x > oldA.x+delta || screen.y < oldA.y-delta || screen.y > oldA.y+delta)) {
    520                                                                 g.setColor(trkPnt.speedLineColor);
    521                                                                 g.drawLine(screen.x, screen.y, screen.x + dir[trkPnt.dir][0], screen.y + dir[trkPnt.dir][1]);
    522                                                                 g.drawLine(screen.x, screen.y, screen.x + dir[trkPnt.dir][2], screen.y + dir[trkPnt.dir][3]);
    523                                                                 oldA = screen;
    524                                                         }
    525                                                         old = screen;
    526                                                 }
    527                                         } // end for trkpnt
    528                                 } // end for segment
    529                         } // end for trk
    530                 } // end if lines
    531 
    532                 /****************************************************************
    533                 ********** STEP 3d - DRAW LARGE POINTS *************************
    534                 ****************************************************************/
    535                 if (large) {
    536                         g.setColor(neutralColor);
    537                         for (GpxTrack trk : data.tracks) {
    538                                 for (Collection<WayPoint> segment : trk.trackSegs) {
    539                                         for (WayPoint trkPnt : segment) {
    540                                                 if (Double.isNaN(trkPnt.latlon.lat()) || Double.isNaN(trkPnt.latlon.lon()))
    541                                                         continue;
    542                                                 Point screen = mv.getPoint(trkPnt.eastNorth);
    543                                                         g.fillRect(screen.x-1, screen.y-1, 3, 3);
    544                                         } // end for trkpnt
    545                                 } // end for segment
    546                         } // end for trk
    547                 } // end if large
    548 
    549                 /****************************************************************
    550                 ********** STEP 3e - DRAW SMALL POINTS FOR LINES ***************
    551                 ****************************************************************/
    552                 if (!large && lines){
    553                         g.setColor(neutralColor);
    554                         for (GpxTrack trk : data.tracks) {
    555                                 for (Collection<WayPoint> segment : trk.trackSegs) {
    556                                         for (WayPoint trkPnt : segment) {
    557                                                 if (Double.isNaN(trkPnt.latlon.lat()) || Double.isNaN(trkPnt.latlon.lon()))
    558                                                         continue;
    559                                                 if (!trkPnt.drawLine) {
    560                                                         Point screen = mv.getPoint(trkPnt.eastNorth);
    561                                                 g.drawRect(screen.x, screen.y, 0, 0);
    562                                         }
    563                                         } // end for trkpnt
    564                                 } // end for segment
    565                         } // end for trk
    566                 } // end if large
    567 
    568                 /****************************************************************
    569                 ********** STEP 3f - DRAW SMALL POINTS INSTEAD OF LINES ********
    570                 ****************************************************************/
    571                 if (!large && !lines){
    572                         g.setColor(neutralColor);
    573                         for (GpxTrack trk : data.tracks) {
    574                                 for (Collection<WayPoint> segment : trk.trackSegs) {
    575                                         for (WayPoint trkPnt : segment) {
    576                                                 if (Double.isNaN(trkPnt.latlon.lat()) || Double.isNaN(trkPnt.latlon.lon()))
    577                                                         continue;
    578                                                 Point screen = mv.getPoint(trkPnt.eastNorth);
    579                                                 g.drawRect(screen.x, screen.y, 0, 0);
    580                                         } // end for trkpnt
    581                                 } // end for segment
    582                         } // end for trk
    583                 } // end if large
    584 
    585                 //Long duration = System.currentTimeMillis() - startTime;
    586                 //System.out.println(duration);
    587         } // end paint
    588 
    589         @Override public void visitBoundingBox(BoundingXYVisitor v) {
    590                 for (WayPoint p : data.waypoints)
    591                         v.visit(p.eastNorth);
    592 
    593                 for (GpxRoute rte : data.routes) {
    594                         Collection<WayPoint> r = rte.routePoints;
    595                         for (WayPoint p : r) {
    596                                 v.visit(p.eastNorth);
    597                         }
    598                 }
    599 
    600                 for (GpxTrack trk : data.tracks) {
    601                         for (Collection<WayPoint> seg : trk.trackSegs) {
    602                                 for (WayPoint p : seg) {
    603                                         v.visit(p.eastNorth);
    604                                 }
    605                         }
    606                 }
    607         }
    608 
    609         public class UploadTraceAction extends AbstractAction {
    610                 public UploadTraceAction() {
    611                         super(tr("Upload this trace..."), ImageProvider.get("uploadtrace"));
    612                 }
    613                 public void actionPerformed(ActionEvent e) {
    614                         JPanel msg = new JPanel(new GridBagLayout());
    615                         msg.add(new JLabel(tr("<html>This functionality has been added only recently. Please<br>"+
    616                         "use with care and check if it works as expected.</html>")), GBC.eop());
    617                         ButtonGroup bg = new ButtonGroup();
    618                         JRadioButton c1 = null;
    619                         JRadioButton c2 = null;
    620 
    621                         //TODO
    622                         //check whether data comes from server
    623                         //check whether data changed sind last save/open
    624 
    625                         c1 = new JRadioButton(tr("Upload track filtered by JOSM"), true);
    626                         c2 = new JRadioButton(tr("Upload raw file: "), false);
    627                         c2.setEnabled(false);
    628                         c1.setEnabled(false);
    629                         bg.add(c1);
    630                         bg.add(c2);
    631 
    632                         msg.add(c1, GBC.eol());
    633                         msg.add(c2, GBC.eop());
    634 
    635 
    636                         JLabel description = new JLabel((String) data.attr.get("desc"));
    637                         JTextField tags = new JTextField();
    638                         tags.setText((String) data.attr.get("keywords"));
    639                         msg.add(new JLabel(tr("Description:")), GBC.std());
    640                         msg.add(description, GBC.eol().fill(GBC.HORIZONTAL));
    641                         msg.add(new JLabel(tr("Tags (keywords in GPX):")), GBC.std());
    642                         msg.add(tags, GBC.eol().fill(GBC.HORIZONTAL));
    643                         JCheckBox c3 = new JCheckBox("public");
    644                         msg.add(c3, GBC.eop());
    645                         msg.add(new JLabel("Please ensure that you don't upload your traces twice."), GBC.eop());
    646 
    647                         int answer = JOptionPane.showConfirmDialog(Main.parent, msg, tr("GPX-Upload"), JOptionPane.OK_CANCEL_OPTION);
    648                         if (answer == JOptionPane.OK_OPTION)
    649                         {
    650                                 try {
    651                                         String version = Main.pref.get("osm-server.version", "0.5");
    652                                         URL url = new URL(Main.pref.get("osm-server.url") + "/" + version + "/gpx/create");
    653 
    654                                         // create a boundary string
    655                                         String boundary = MultiPartFormOutputStream.createBoundary();
    656                                         URLConnection urlConn = MultiPartFormOutputStream.createConnection(url);
    657                                         urlConn.setRequestProperty("Accept", "*/*");
    658                                         urlConn.setRequestProperty("Content-Type", MultiPartFormOutputStream.getContentType(boundary));
    659                                         // set some other request headers...
    660                                         urlConn.setRequestProperty("Connection", "Keep-Alive");
    661                                         urlConn.setRequestProperty("Cache-Control", "no-cache");
    662                                         // no need to connect cuz getOutputStream() does it
    663                                         MultiPartFormOutputStream out = new MultiPartFormOutputStream(urlConn.getOutputStream(), boundary);
    664                                         out.writeField("description", description.getText());
    665                                         out.writeField("tags", tags.getText());
    666                                         out.writeField("public", (c3.getSelectedObjects() != null) ? "1" : "0");
    667                                         // upload a file
    668                                         // out.writeFile("gpx_file", "text/xml", associatedFile);
    669                                         // can also write bytes directly
    670                                         // out.writeFile("myFile", "text/plain", "C:\\test.txt",
    671                                         // "This is some file text.".getBytes("ASCII"));
    672                                         File tmp = File.createTempFile("josm", "tmp.gpx");
    673                                         FileOutputStream outs = new FileOutputStream(tmp);
    674                                         new GpxWriter(outs).write(data);
    675                                         outs.close();
    676                                         FileInputStream ins = new FileInputStream(tmp);
    677                                         new GpxWriter(System.out).write(data);
    678                                         out.writeFile("gpx_file", "text/xml", data.storageFile.getName(), ins);
    679                                         out.close();
    680                                         tmp.delete();
    681                                         // read response from server
    682                                         BufferedReader in = new BufferedReader(new InputStreamReader(urlConn.getInputStream()));
    683                                         String line = "";
    684                                         while((line = in.readLine()) != null) {
    685                                                 System.out.println(line);
    686                                         }
    687                                         in.close();
    688 
    689                                         //TODO check response
    690                                         /*                  int retCode = urlConn.getResponseCode();
    691                                         System.out.println("got return: " + retCode);
    692                                         String retMsg = urlConn.getResponseMessage();
    693                                         urlConn.disconnect();
    694                                         if (retCode != 200) {
    695                                                 // Look for a detailed error message from the server
    696                                                 if (urlConn.getHeaderField("Error") != null)
    697                                                         retMsg += "\n" + urlConn.getHeaderField("Error");
    698 
    699                                                 // Report our error
    700                                                 ByteArrayOutputStream o = new ByteArrayOutputStream();
    701                                                 System.out.println(new String(o.toByteArray(), "UTF-8").toString());
    702                                                 throw new RuntimeException(retCode+" "+retMsg);
    703                                         }
    704                                         */
    705                                 } catch (UnknownHostException ex) {
    706                                         throw new RuntimeException(tr("Unknown host")+": "+ex.getMessage(), ex);
    707                                 } catch (Exception ex) {
    708                                         //if (cancel)
    709                                         //  return; // assume cancel
    710                                         if (ex instanceof RuntimeException)
    711                                                 throw (RuntimeException)ex;
    712                                         throw new RuntimeException(ex.getMessage(), ex);
    713                                 }
    714                         }
    715                 }
    716         }
    717 
    718         public class ConvertToDataLayerAction extends AbstractAction {
    719                 public ConvertToDataLayerAction() {
    720                         super(tr("Convert to data layer"), ImageProvider.get("converttoosm"));
    721                 }
    722                 public void actionPerformed(ActionEvent e) {
    723                         JPanel msg = new JPanel(new GridBagLayout());
    724                         msg.add(new JLabel(tr("<html>Upload of unprocessed GPS data as map data is considered harmful.<br>If you want to upload traces, look here:")), GBC.eol());
    725                         msg.add(new UrlLabel(tr("http://www.openstreetmap.org/traces")), GBC.eop());
    726                         if (!DontShowAgainInfo.show("convert_to_data", msg))
    727                                 return;
    728                         DataSet ds = new DataSet();
    729                         for (GpxTrack trk : data.tracks) {
    730                                 for (Collection<WayPoint> segment : trk.trackSegs) {
    731                                         Way w = new Way();
    732                                         for (WayPoint p : segment) {
    733                                                 Node n = new Node(p.latlon);
    734                                                 String timestr = p.getString("time");
    735                                                 if(timestr != null)
    736                                                 {
    737                                                         timestr = timestr.replace("Z","+00:00");
    738                                                         n.timestamp = timestr;
    739                                                 }
    740                                                 ds.nodes.add(n);
    741                                                 w.nodes.add(n);
    742                                         }
    743                                         ds.ways.add(w);
    744                                 }
    745                         }
    746                         Main.main.addLayer(new OsmDataLayer(ds, tr("Converted from: {0}", GpxLayer.this.name), null));
    747                         Main.main.removeLayer(GpxLayer.this);
    748                 }
    749         }
     402            computeCacheMaxLineLengthUsed = maxLineLength;
     403            computeCacheInSync = false;
     404            computeCacheColorUsed = neutralColor;
     405            computeCacheColored = colored;
     406        }
     407
     408        /****************************************************************
     409        ********** STEP 2b - RE-COMPUTE CACHE DATA *********************
     410        ****************************************************************/
     411        if (!computeCacheInSync) { // don't compute if the cache is good
     412            WayPoint oldWp = null;
     413            for (GpxTrack trk : data.tracks) {
     414                if (!forceLines) { // don't draw lines between segments, unless forced to
     415                    oldWp = null;
     416                }
     417                for (Collection<WayPoint> segment : trk.trackSegs) {
     418                    for (WayPoint trkPnt : segment) {
     419                        if (Double.isNaN(trkPnt.latlon.lat()) || Double.isNaN(trkPnt.latlon.lon())) {
     420                            continue;
     421                        }
     422                        if (oldWp != null) {
     423                            double dist = trkPnt.latlon.greatCircleDistance(oldWp.latlon);
     424                            double dtime = trkPnt.time - oldWp.time;
     425                            double vel = dist/dtime;
     426
     427                            if (!colored) {
     428                                trkPnt.speedLineColor = neutralColor;
     429                            } else if (dtime <= 0 || vel < 0 || vel > 36) { // attn: bad case first
     430                                trkPnt.speedLineColor = colors[255];
     431                            } else {
     432                                trkPnt.speedLineColor = colors[(int) (7*vel)];
     433                            }
     434                            if (maxLineLength == -1 || dist <= maxLineLength) {
     435                                trkPnt.drawLine = true;
     436                                trkPnt.dir = (int)(Math.atan2(-trkPnt.eastNorth.north()+oldWp.eastNorth.north(), trkPnt.eastNorth.east()-oldWp.eastNorth.east()) / Math.PI * 4 + 3.5); // crude but works
     437                            } else {
     438                                trkPnt.drawLine = false;
     439                            }
     440                        } else { // make sure we reset outdated data
     441                            trkPnt.speedLineColor = colors[255];
     442                            trkPnt.drawLine = false;
     443                        }
     444                        oldWp = trkPnt;
     445                    }
     446                }
     447            }
     448            computeCacheInSync = true;
     449        }
     450
     451        /****************************************************************
     452        ********** STEP 3a - DRAW LINES ********************************
     453        ****************************************************************/
     454        if (lines) {
     455        Point old = null;
     456        for (GpxTrack trk : data.tracks) {
     457            for (Collection<WayPoint> segment : trk.trackSegs) {
     458                for (WayPoint trkPnt : segment) {
     459                    if (Double.isNaN(trkPnt.latlon.lat()) || Double.isNaN(trkPnt.latlon.lon()))
     460                        continue;
     461                    Point screen = mv.getPoint(trkPnt.eastNorth);
     462                        if (trkPnt.drawLine) {
     463                            // skip points that are on the same screenposition
     464                            if (old != null && ((old.x != screen.x) || (old.y != screen.y))) {
     465                                g.setColor(trkPnt.speedLineColor);
     466                                g.drawLine(old.x, old.y, screen.x, screen.y);
     467                            }
     468                        }
     469                        old = screen;
     470                    } // end for trkpnt
     471                } // end for segment
     472            } // end for trk
     473        } // end if lines
     474
     475        /****************************************************************
     476        ********** STEP 3b - DRAW NICE ARROWS **************************
     477        ****************************************************************/
     478        if (lines && direction && !alternatedirection) {
     479            Point old = null;
     480            Point oldA = null; // last arrow painted
     481            for (GpxTrack trk : data.tracks) {
     482                for (Collection<WayPoint> segment : trk.trackSegs) {
     483                    for (WayPoint trkPnt : segment) {
     484                        if (Double.isNaN(trkPnt.latlon.lat()) || Double.isNaN(trkPnt.latlon.lon()))
     485                            continue;
     486                        if (trkPnt.drawLine) {
     487                            Point screen = mv.getPoint(trkPnt.eastNorth);
     488                            // skip points that are on the same screenposition
     489                            if (old != null && (oldA == null || screen.x < oldA.x-delta || screen.x > oldA.x+delta || screen.y < oldA.y-delta || screen.y > oldA.y+delta)) {
     490                                g.setColor(trkPnt.speedLineColor);
     491                                double t = Math.atan2(screen.y-old.y, screen.x-old.x) + Math.PI;
     492                                g.drawLine(screen.x,screen.y, (int)(screen.x + 10*Math.cos(t-PHI)), (int)(screen.y
     493                                + 10*Math.sin(t-PHI)));
     494                                g.drawLine(screen.x,screen.y, (int)(screen.x + 10*Math.cos(t+PHI)), (int)(screen.y
     495                                + 10*Math.sin(t+PHI)));
     496                                oldA = screen;
     497                            }
     498                            old = screen;
     499                        }
     500                    } // end for trkpnt
     501                } // end for segment
     502            } // end for trk
     503        } // end if lines
     504
     505        /****************************************************************
     506        ********** STEP 3c - DRAW FAST ARROWS **************************
     507        ****************************************************************/
     508        if (lines && direction && alternatedirection) {
     509            Point old = null;
     510            Point oldA = null; // last arrow painted
     511            for (GpxTrack trk : data.tracks) {
     512                for (Collection<WayPoint> segment : trk.trackSegs) {
     513                    for (WayPoint trkPnt : segment) {
     514                        if (Double.isNaN(trkPnt.latlon.lat()) || Double.isNaN(trkPnt.latlon.lon()))
     515                            continue;
     516                        if (trkPnt.drawLine) {
     517                            Point screen = mv.getPoint(trkPnt.eastNorth);
     518                            // skip points that are on the same screenposition
     519                            if (old != null && (oldA == null || screen.x < oldA.x-delta || screen.x > oldA.x+delta || screen.y < oldA.y-delta || screen.y > oldA.y+delta)) {
     520                                g.setColor(trkPnt.speedLineColor);
     521                                g.drawLine(screen.x, screen.y, screen.x + dir[trkPnt.dir][0], screen.y + dir[trkPnt.dir][1]);
     522                                g.drawLine(screen.x, screen.y, screen.x + dir[trkPnt.dir][2], screen.y + dir[trkPnt.dir][3]);
     523                                oldA = screen;
     524                            }
     525                            old = screen;
     526                        }
     527                    } // end for trkpnt
     528                } // end for segment
     529            } // end for trk
     530        } // end if lines
     531
     532        /****************************************************************
     533        ********** STEP 3d - DRAW LARGE POINTS *************************
     534        ****************************************************************/
     535        if (large) {
     536            g.setColor(neutralColor);
     537            for (GpxTrack trk : data.tracks) {
     538                for (Collection<WayPoint> segment : trk.trackSegs) {
     539                    for (WayPoint trkPnt : segment) {
     540                        if (Double.isNaN(trkPnt.latlon.lat()) || Double.isNaN(trkPnt.latlon.lon()))
     541                            continue;
     542                        Point screen = mv.getPoint(trkPnt.eastNorth);
     543                            g.fillRect(screen.x-1, screen.y-1, 3, 3);
     544                    } // end for trkpnt
     545                } // end for segment
     546            } // end for trk
     547        } // end if large
     548
     549        /****************************************************************
     550        ********** STEP 3e - DRAW SMALL POINTS FOR LINES ***************
     551        ****************************************************************/
     552        if (!large && lines){
     553            g.setColor(neutralColor);
     554            for (GpxTrack trk : data.tracks) {
     555                for (Collection<WayPoint> segment : trk.trackSegs) {
     556                    for (WayPoint trkPnt : segment) {
     557                        if (Double.isNaN(trkPnt.latlon.lat()) || Double.isNaN(trkPnt.latlon.lon()))
     558                            continue;
     559                        if (!trkPnt.drawLine) {
     560                            Point screen = mv.getPoint(trkPnt.eastNorth);
     561                        g.drawRect(screen.x, screen.y, 0, 0);
     562                    }
     563                    } // end for trkpnt
     564                } // end for segment
     565            } // end for trk
     566        } // end if large
     567
     568        /****************************************************************
     569        ********** STEP 3f - DRAW SMALL POINTS INSTEAD OF LINES ********
     570        ****************************************************************/
     571        if (!large && !lines){
     572            g.setColor(neutralColor);
     573            for (GpxTrack trk : data.tracks) {
     574                for (Collection<WayPoint> segment : trk.trackSegs) {
     575                    for (WayPoint trkPnt : segment) {
     576                        if (Double.isNaN(trkPnt.latlon.lat()) || Double.isNaN(trkPnt.latlon.lon()))
     577                            continue;
     578                        Point screen = mv.getPoint(trkPnt.eastNorth);
     579                        g.drawRect(screen.x, screen.y, 0, 0);
     580                    } // end for trkpnt
     581                } // end for segment
     582            } // end for trk
     583        } // end if large
     584
     585        //Long duration = System.currentTimeMillis() - startTime;
     586        //System.out.println(duration);
     587    } // end paint
     588
     589    @Override public void visitBoundingBox(BoundingXYVisitor v) {
     590        for (WayPoint p : data.waypoints)
     591            v.visit(p.eastNorth);
     592
     593        for (GpxRoute rte : data.routes) {
     594            Collection<WayPoint> r = rte.routePoints;
     595            for (WayPoint p : r) {
     596                v.visit(p.eastNorth);
     597            }
     598        }
     599
     600        for (GpxTrack trk : data.tracks) {
     601            for (Collection<WayPoint> seg : trk.trackSegs) {
     602                for (WayPoint p : seg) {
     603                    v.visit(p.eastNorth);
     604                }
     605            }
     606        }
     607    }
     608
     609    public class UploadTraceAction extends AbstractAction {
     610        public UploadTraceAction() {
     611            super(tr("Upload this trace..."), ImageProvider.get("uploadtrace"));
     612        }
     613        public void actionPerformed(ActionEvent e) {
     614            JPanel msg = new JPanel(new GridBagLayout());
     615            msg.add(new JLabel(tr("<html>This functionality has been added only recently. Please<br>"+
     616            "use with care and check if it works as expected.</html>")), GBC.eop());
     617            ButtonGroup bg = new ButtonGroup();
     618            JRadioButton c1 = null;
     619            JRadioButton c2 = null;
     620
     621            //TODO
     622            //check whether data comes from server
     623            //check whether data changed sind last save/open
     624
     625            c1 = new JRadioButton(tr("Upload track filtered by JOSM"), true);
     626            c2 = new JRadioButton(tr("Upload raw file: "), false);
     627            c2.setEnabled(false);
     628            c1.setEnabled(false);
     629            bg.add(c1);
     630            bg.add(c2);
     631
     632            msg.add(c1, GBC.eol());
     633            msg.add(c2, GBC.eop());
     634
     635
     636            JLabel description = new JLabel((String) data.attr.get("desc"));
     637            JTextField tags = new JTextField();
     638            tags.setText((String) data.attr.get("keywords"));
     639            msg.add(new JLabel(tr("Description:")), GBC.std());
     640            msg.add(description, GBC.eol().fill(GBC.HORIZONTAL));
     641            msg.add(new JLabel(tr("Tags (keywords in GPX):")), GBC.std());
     642            msg.add(tags, GBC.eol().fill(GBC.HORIZONTAL));
     643            JCheckBox c3 = new JCheckBox("public");
     644            msg.add(c3, GBC.eop());
     645            msg.add(new JLabel("Please ensure that you don't upload your traces twice."), GBC.eop());
     646
     647            int answer = JOptionPane.showConfirmDialog(Main.parent, msg, tr("GPX-Upload"), JOptionPane.OK_CANCEL_OPTION);
     648            if (answer == JOptionPane.OK_OPTION)
     649            {
     650                try {
     651                    String version = Main.pref.get("osm-server.version", "0.5");
     652                    URL url = new URL(Main.pref.get("osm-server.url") + "/" + version + "/gpx/create");
     653
     654                    // create a boundary string
     655                    String boundary = MultiPartFormOutputStream.createBoundary();
     656                    URLConnection urlConn = MultiPartFormOutputStream.createConnection(url);
     657                    urlConn.setRequestProperty("Accept", "*/*");
     658                    urlConn.setRequestProperty("Content-Type", MultiPartFormOutputStream.getContentType(boundary));
     659                    // set some other request headers...
     660                    urlConn.setRequestProperty("Connection", "Keep-Alive");
     661                    urlConn.setRequestProperty("Cache-Control", "no-cache");
     662                    // no need to connect cuz getOutputStream() does it
     663                    MultiPartFormOutputStream out = new MultiPartFormOutputStream(urlConn.getOutputStream(), boundary);
     664                    out.writeField("description", description.getText());
     665                    out.writeField("tags", tags.getText());
     666                    out.writeField("public", (c3.getSelectedObjects() != null) ? "1" : "0");
     667                    // upload a file
     668                    // out.writeFile("gpx_file", "text/xml", associatedFile);
     669                    // can also write bytes directly
     670                    // out.writeFile("myFile", "text/plain", "C:\\test.txt",
     671                    // "This is some file text.".getBytes("ASCII"));
     672                    File tmp = File.createTempFile("josm", "tmp.gpx");
     673                    FileOutputStream outs = new FileOutputStream(tmp);
     674                    new GpxWriter(outs).write(data);
     675                    outs.close();
     676                    FileInputStream ins = new FileInputStream(tmp);
     677                    new GpxWriter(System.out).write(data);
     678                    out.writeFile("gpx_file", "text/xml", data.storageFile.getName(), ins);
     679                    out.close();
     680                    tmp.delete();
     681                    // read response from server
     682                    BufferedReader in = new BufferedReader(new InputStreamReader(urlConn.getInputStream()));
     683                    String line = "";
     684                    while((line = in.readLine()) != null) {
     685                        System.out.println(line);
     686                    }
     687                    in.close();
     688
     689                    //TODO check response
     690                    /*                  int retCode = urlConn.getResponseCode();
     691                    System.out.println("got return: " + retCode);
     692                    String retMsg = urlConn.getResponseMessage();
     693                    urlConn.disconnect();
     694                    if (retCode != 200) {
     695                        // Look for a detailed error message from the server
     696                        if (urlConn.getHeaderField("Error") != null)
     697                            retMsg += "\n" + urlConn.getHeaderField("Error");
     698
     699                        // Report our error
     700                        ByteArrayOutputStream o = new ByteArrayOutputStream();
     701                        System.out.println(new String(o.toByteArray(), "UTF-8").toString());
     702                        throw new RuntimeException(retCode+" "+retMsg);
     703                    }
     704                    */
     705                } catch (UnknownHostException ex) {
     706                    throw new RuntimeException(tr("Unknown host")+": "+ex.getMessage(), ex);
     707                } catch (Exception ex) {
     708                    //if (cancel)
     709                    //  return; // assume cancel
     710                    if (ex instanceof RuntimeException)
     711                        throw (RuntimeException)ex;
     712                    throw new RuntimeException(ex.getMessage(), ex);
     713                }
     714            }
     715        }
     716    }
     717
     718    public class ConvertToDataLayerAction extends AbstractAction {
     719        public ConvertToDataLayerAction() {
     720            super(tr("Convert to data layer"), ImageProvider.get("converttoosm"));
     721        }
     722        public void actionPerformed(ActionEvent e) {
     723            JPanel msg = new JPanel(new GridBagLayout());
     724            msg.add(new JLabel(tr("<html>Upload of unprocessed GPS data as map data is considered harmful.<br>If you want to upload traces, look here:")), GBC.eol());
     725            msg.add(new UrlLabel(tr("http://www.openstreetmap.org/traces")), GBC.eop());
     726            if (!DontShowAgainInfo.show("convert_to_data", msg))
     727                return;
     728            DataSet ds = new DataSet();
     729            for (GpxTrack trk : data.tracks) {
     730                for (Collection<WayPoint> segment : trk.trackSegs) {
     731                    Way w = new Way();
     732                    for (WayPoint p : segment) {
     733                        Node n = new Node(p.latlon);
     734                        String timestr = p.getString("time");
     735                        if(timestr != null)
     736                        {
     737                            timestr = timestr.replace("Z","+00:00");
     738                            n.timestamp = timestr;
     739                        }
     740                        ds.nodes.add(n);
     741                        w.nodes.add(n);
     742                    }
     743                    ds.ways.add(w);
     744                }
     745            }
     746            Main.main.addLayer(new OsmDataLayer(ds, tr("Converted from: {0}", GpxLayer.this.name), null));
     747            Main.main.removeLayer(GpxLayer.this);
     748        }
     749    }
    750750
    751751    /**
    752752     * Action that issues a series of download requests to the API, following the GPX track.
    753      * 
     753     *
    754754     * @author fred
    755755     */
     
    762762            JList buffer = new JList(new String[] { "50 metres", "500 metres", "5000 metres" });
    763763            JList maxRect = new JList(new String[] { "1 sq km", "5 sq km", "10 sq km", "20 sq km" });
    764            
     764
    765765            msg.add(new JLabel(tr("Download everything within:")), GBC.eol());
    766766            msg.add(buffer, GBC.eol());
    767767            msg.add(new JLabel(tr("Maximum area per request:")), GBC.eol());
    768768            msg.add(maxRect, GBC.eol());
    769            
    770             if (JOptionPane.showConfirmDialog(Main.parent, msg, 
    771                 tr("Download from OSM along this track"), 
     769
     770            if (JOptionPane.showConfirmDialog(Main.parent, msg,
     771                tr("Download from OSM along this track"),
    772772                JOptionPane.OK_CANCEL_OPTION) == JOptionPane.CANCEL_OPTION) {
    773773                return;
    774            
     774
    775775            }
    776776
     
    781781            double latsum = 0;
    782782            int latcnt = 0;
    783            
     783
    784784            for (GpxTrack trk : data.tracks) {
    785785                for (Collection<WayPoint> segment : trk.trackSegs) {
     
    790790                }
    791791            }
    792            
     792
    793793            double avglat = latsum / latcnt;
    794794            double scale = Math.cos(Math.toRadians(avglat));
     
    797797             * Compute buffer zone extents and maximum bounding box size. Note how the
    798798             * maximum we ever offer is a bbox area of 0.002, while the API theoretically
    799              * supports 0.25, but as soon as you touch any built-up area, that kind of 
    800              * bounding box will download forever and then stop because it has more than 
     799             * supports 0.25, but as soon as you touch any built-up area, that kind of
     800             * bounding box will download forever and then stop because it has more than
    801801             * 50k nodes.
    802802             */
     
    809809            }
    810810            buffer_x = buffer_y / scale;
    811            
     811
    812812            double max_area;
    813813            switch(maxRect.getSelectedIndex()) {
     
    820820            Area a = new Area();
    821821            Rectangle2D r = new Rectangle2D.Double();
    822            
     822
    823823            /*
    824824             * Collect the combined area of all gpx points plus buffer zones around them.
     
    836836                }
    837837            }
    838            
     838
    839839            /*
    840840             * Area "a" now contains the hull that we would like to download data for.
     
    858858             * actually has something in it.
    859859             */
    860            
     860
    861861            List<Rectangle2D> toDownload = new ArrayList<Rectangle2D>();
    862            
     862
    863863            addToDownload(a, a.getBounds(), toDownload, max_area);
    864            
     864
    865865            msg = new JPanel(new GridBagLayout());
    866                        
     866
    867867            msg.add(new JLabel(tr("<html>This action will require {0} individual<br>download requests. Do you wish<br>to continue?</html>",
    868868                toDownload.size())), GBC.eol());
    869            
    870             if (JOptionPane.showConfirmDialog(Main.parent, msg, 
    871                 tr("Download from OSM along this track"), 
     869
     870            if (JOptionPane.showConfirmDialog(Main.parent, msg,
     871                tr("Download from OSM along this track"),
    872872                JOptionPane.OK_CANCEL_OPTION) == JOptionPane.CANCEL_OPTION) {
    873873                return;
    874874            }
    875            
     875
    876876            // FIXME: DownloadTask's "please wait" dialog should display the number of
    877877            // downloads left, and "cancel" needs to be honoured. An error along the way
     
    883883        }
    884884    }
    885    
     885
    886886    private static void addToDownload(Area a, Rectangle2D r, Collection<Rectangle2D> results, double max_area) {
    887887        Area tmp = new Area(r);
     
    909909        }
    910910    }
    911    
    912         /**
    913         * Makes a new marker layer derived from this GpxLayer containing at least one
    914         * audio marker which the given audio file is associated with.
    915         * Markers are derived from the following
    916         * (a) explict waypoints in the GPX layer, or
    917         * (b) named trackpoints in the GPX layer, or
    918         * (c) (in future) voice recognised markers in the sound recording
    919         * (d) a single marker at the beginning of the track
    920         * @param wavFile : the file to be associated with the markers in the new marker layer
    921         */
    922         private void importAudio(File wavFile) {
    923                 String uri = "file:".concat(wavFile.getAbsolutePath());
    924                 MarkerLayer ml = new MarkerLayer(new GpxData(), tr("Audio markers from {0}", name), associatedFile, me);
    925 
    926                 Collection<WayPoint> waypoints = new ArrayList<WayPoint>();
    927                 boolean timedMarkersOmitted = false;
    928                 boolean untimedMarkersOmitted = false;
    929                 double snapDistance = Main.pref.getDouble("marker.audiofromuntimedwaypoints.distance", 1.0e-3); /* about 25m */
    930 
    931                 // determine time of first point in track
    932                 double firstTime = -1.0;
    933                 if (data.tracks != null && ! data.tracks.isEmpty()) {
    934                         for (GpxTrack track : data.tracks) {
    935                                 if (track.trackSegs == null) continue;
    936                                 for (Collection<WayPoint> seg : track.trackSegs) {
    937                                         for (WayPoint w : seg) {
    938                                                 firstTime = w.time;
    939                                                 break;
    940                                         }
    941                                         if (firstTime >= 0.0) break;
    942                                 }
    943                                 if (firstTime >= 0.0) break;
    944                         }
    945                 }
    946                 if (firstTime < 0.0) {
    947                         JOptionPane.showMessageDialog(Main.parent, tr("No GPX track available in layer to associate audio with."));
    948                         return;
    949                 }
    950 
    951                 // (a) try explicit timestamped waypoints - unless suppressed
    952                 if (Main.pref.getBoolean("marker.audiofromexplicitwaypoints", true) &&
    953                         data.waypoints != null && ! data.waypoints.isEmpty())
    954                 {
    955                         for (WayPoint w : data.waypoints) {
    956                                 if (w.time > firstTime) {
    957                                         waypoints.add(w);
    958                                 } else if (w.time > 0.0) {
    959                                         timedMarkersOmitted = true;
    960                                 }
    961                         }
    962                 }
    963 
    964                 // (b) try explicit waypoints without timestamps - unless suppressed
    965                 if (Main.pref.getBoolean("marker.audiofromuntimedwaypoints", true) &&
    966                         data.waypoints != null && ! data.waypoints.isEmpty())
    967                 {
    968                         for (WayPoint w : data.waypoints) {
    969                                 if (waypoints.contains(w)) { continue; }
    970                                 WayPoint wNear = nearestPointOnTrack(w.eastNorth, snapDistance);
    971                                 if (wNear != null) {
    972                                         WayPoint wc = new WayPoint(w.latlon);
    973                                         wc.time = wNear.time;
    974                                         if (w.attr.containsKey("name")) wc.attr.put("name", w.getString("name"));
    975                                         waypoints.add(wc);
    976                                 } else {
    977                                         untimedMarkersOmitted = true;
    978                                 }
    979                         }
    980                 }
    981 
    982                 // (c) use explicitly named track points, again unless suppressed
    983                 if ((Main.pref.getBoolean("marker.audiofromnamedtrackpoints", false)) &&
    984                         data.tracks != null && ! data.tracks.isEmpty())
    985                 {
    986                         for (GpxTrack track : data.tracks) {
    987                                 if (track.trackSegs == null) continue;
    988                                 for (Collection<WayPoint> seg : track.trackSegs) {
    989                                         for (WayPoint w : seg) {
    990                                                 if (w.attr.containsKey("name") || w.attr.containsKey("desc")) {
    991                                                         waypoints.add(w);
    992                                                 }
    993                                         }
    994                                 }
    995                         }
    996                 }
    997 
    998                 // (d) analyse audio for spoken markers here, in due course
    999 
    1000                 // (e) simply add a single marker at the start of the track
    1001                 if ((Main.pref.getBoolean("marker.audiofromstart") || waypoints.isEmpty()) &&
    1002                         data.tracks != null && ! data.tracks.isEmpty())
    1003                 {
    1004                         boolean gotOne = false;
    1005                         for (GpxTrack track : data.tracks) {
    1006                                 if (track.trackSegs == null) continue;
    1007                                 for (Collection<WayPoint> seg : track.trackSegs) {
    1008                                         for (WayPoint w : seg) {
    1009                                                 WayPoint wStart = new WayPoint(w.latlon);
    1010                                                 wStart.attr.put("name", "start");
    1011                                                 wStart.time = w.time;
    1012                                                 waypoints.add(wStart);
    1013                                                 gotOne = true;
    1014                                                 break;
    1015                                         }
    1016                                         if (gotOne) break;
    1017                                 }
    1018                                 if (gotOne) break;
    1019                         }
    1020                 }
    1021 
    1022                 /* we must have got at least one waypoint now */
    1023 
    1024                 Collections.sort((ArrayList<WayPoint>) waypoints, new Comparator<WayPoint>() {
    1025                         public int compare(WayPoint a, WayPoint b) {
    1026                                 return a.time <= b.time ? -1 : 1;
    1027                         }
    1028                 });
    1029 
    1030                 firstTime = -1.0; /* this time of the first waypoint, not first trackpoint */
    1031                 for (WayPoint w : waypoints) {
    1032                         if (firstTime < 0.0) firstTime = w.time;
    1033                         double offset = w.time - firstTime;
    1034                         String name;
    1035                         if (w.attr.containsKey("name"))
    1036                                 name = w.getString("name");
    1037                         else if (w.attr.containsKey("desc"))
    1038                                 name = w.getString("desc");
    1039                         else
    1040                                 name = AudioMarker.inventName(offset);
    1041                         AudioMarker am = AudioMarker.create(w.latlon,
    1042                                         name, uri, ml, w.time, offset);
    1043                         ml.data.add(am);
    1044                 }
    1045                 Main.main.addLayer(ml);
    1046 
    1047                 if (timedMarkersOmitted) {
    1048                         JOptionPane.showMessageDialog(Main.parent,
    1049                         tr("Some waypoints with timestamps from before the start of the track were omitted."));
    1050                 }
    1051                 if (untimedMarkersOmitted) {
    1052                         JOptionPane.showMessageDialog(Main.parent,
    1053                         tr("Some waypoints which were too far from the track to sensibly estimate their time were omitted."));
    1054                 }
    1055         }
    1056 
    1057         /**
    1058         * Makes a WayPoint at the projection of point P onto the track providing P is
    1059         * less than tolerance away from the track
    1060 
    1061         * @param P : the point to determine the projection for
    1062         * @param tolerance : must be no further than this from the track
    1063         * @return the closest point on the track to P, which may be the
    1064         * first or last point if off the end of a segment, or may be null if
    1065         * nothing close enough
    1066         */
    1067         public WayPoint nearestPointOnTrack(EastNorth P, double tolerance) {
    1068                 /*
    1069                 * assume the coordinates of P are xp,yp, and those of a section of track
    1070                 * between two trackpoints are R=xr,yr and S=xs,ys. Let N be the projected point.
    1071                 *
    1072                 * The equation of RS is Ax + By + C = 0 where
    1073                 * A = ys - yr
    1074                 * B = xr - xs
    1075                 * C = - Axr - Byr
    1076                 *
    1077                 * Also, note that the distance RS^2 is A^2 + B^2
    1078                 *
    1079                 * If RS^2 == 0.0 ignore the degenerate section of track
    1080                 *
    1081                 * PN^2 = (Axp + Byp + C)^2 / RS^2
    1082                 * that is the distance from P to the line
    1083                 *
    1084                 * so if PN^2 is less than PNmin^2 (initialized to tolerance) we can reject
    1085                 * the line; otherwise...
    1086                 * determine if the projected poijnt lies within the bounds of the line:
    1087                 * PR^2 - PN^2 <= RS^2 and PS^2 - PN^2 <= RS^2
    1088                 *
    1089                 * where PR^2 = (xp - xr)^2 + (yp-yr)^2
    1090                 * and   PS^2 = (xp - xs)^2 + (yp-ys)^2
    1091                 *
    1092                 * If so, calculate N as
    1093                 * xn = xr + (RN/RS) B
    1094                 * yn = y1 + (RN/RS) A
    1095                 *
    1096                 * where RN = sqrt(PR^2 - PN^2)
    1097                 */
    1098 
    1099                 double PNminsq = tolerance * tolerance;
    1100                 EastNorth bestEN = null;
    1101                 double bestTime = 0.0;
    1102                 double px = P.east();
    1103                 double py = P.north();
    1104                 double rx = 0.0, ry = 0.0, sx, sy, x, y;
    1105                 if (data.tracks == null) return null;
    1106                 for (GpxTrack track : data.tracks) {
    1107                         if (track.trackSegs == null) continue;
    1108                         for (Collection<WayPoint> seg : track.trackSegs) {
    1109                                 WayPoint R = null;
    1110                                 for (WayPoint S : seg) {
    1111                                         if (R == null) {
    1112                                                 R = S;
    1113                                                 rx = R.eastNorth.east();
    1114                                                 ry = R.eastNorth.north();
    1115                                                 x = px - rx;
    1116                                                 y = py - ry;
    1117                                                 double PRsq = x * x + y * y;
    1118                                                 if (PRsq < PNminsq) {
    1119                                                         PNminsq = PRsq;
    1120                                                         bestEN = R.eastNorth;
    1121                                                         bestTime = R.time;
    1122                                                 }
    1123                                         } else {
    1124                                                 sx = S.eastNorth.east();
    1125                                                 sy = S.eastNorth.north();
    1126                                                 double A = sy - ry;
    1127                                                 double B = rx - sx;
    1128                                                 double C = - A * rx - B * ry;
    1129                                                 double RSsq = A * A + B * B;
    1130                                                 if (RSsq == 0.0) continue;
    1131                                                 double PNsq = A * px + B * py + C;
    1132                                                 PNsq = PNsq * PNsq / RSsq;
    1133                                                 if (PNsq < PNminsq) {
    1134                                                         x = px - rx;
    1135                                                         y = py - ry;
    1136                                                         double PRsq = x * x + y * y;
    1137                                                         x = px - sx;
    1138                                                         y = py - sy;
    1139                                                         double PSsq = x * x + y * y;
    1140                                                         if (PRsq - PNsq <= RSsq && PSsq - PNsq <= RSsq) {
    1141                                                                 double RNoverRS = Math.sqrt((PRsq - PNsq)/RSsq);
    1142                                                                 double nx = rx - RNoverRS * B;
    1143                                                                 double ny = ry + RNoverRS * A;
    1144                                                                 bestEN = new EastNorth(nx, ny);
    1145                                                                 bestTime = R.time + RNoverRS * (S.time - R.time);
    1146                                                                 PNminsq = PNsq;
    1147                                                         }
    1148                                                 }
    1149                                                 R = S;
    1150                                                 rx = sx;
    1151                                                 ry = sy;
    1152                                         }
    1153                                 }
    1154                                 if (R != null) {
    1155                                         /* if there is only one point in the seg, it will do this twice, but no matter */
    1156                                         rx = R.eastNorth.east();
    1157                                         ry = R.eastNorth.north();
    1158                                         x = px - rx;
    1159                                         y = py - ry;
    1160                                         double PRsq = x * x + y * y;
    1161                                         if (PRsq < PNminsq) {
    1162                                                 PNminsq = PRsq;
    1163                                                 bestEN = R.eastNorth;
    1164                                                 bestTime = R.time;
    1165                                         }
    1166                                 }
    1167                         }
    1168                 }
    1169                 if (bestEN == null) return null;
    1170                 WayPoint best = new WayPoint(Main.proj.eastNorth2latlon(bestEN));
    1171                 best.time = bestTime;
    1172                 return best;
    1173         }
     911
     912    /**
     913    * Makes a new marker layer derived from this GpxLayer containing at least one
     914    * audio marker which the given audio file is associated with.
     915    * Markers are derived from the following
     916    * (a) explict waypoints in the GPX layer, or
     917    * (b) named trackpoints in the GPX layer, or
     918    * (c) (in future) voice recognised markers in the sound recording
     919    * (d) a single marker at the beginning of the track
     920    * @param wavFile : the file to be associated with the markers in the new marker layer
     921    */
     922    private void importAudio(File wavFile) {
     923        String uri = "file:".concat(wavFile.getAbsolutePath());
     924        MarkerLayer ml = new MarkerLayer(new GpxData(), tr("Audio markers from {0}", name), associatedFile, me);
     925
     926        Collection<WayPoint> waypoints = new ArrayList<WayPoint>();
     927        boolean timedMarkersOmitted = false;
     928        boolean untimedMarkersOmitted = false;
     929        double snapDistance = Main.pref.getDouble("marker.audiofromuntimedwaypoints.distance", 1.0e-3); /* about 25m */
     930
     931        // determine time of first point in track
     932        double firstTime = -1.0;
     933        if (data.tracks != null && ! data.tracks.isEmpty()) {
     934            for (GpxTrack track : data.tracks) {
     935                if (track.trackSegs == null) continue;
     936                for (Collection<WayPoint> seg : track.trackSegs) {
     937                    for (WayPoint w : seg) {
     938                        firstTime = w.time;
     939                        break;
     940                    }
     941                    if (firstTime >= 0.0) break;
     942                }
     943                if (firstTime >= 0.0) break;
     944            }
     945        }
     946        if (firstTime < 0.0) {
     947            JOptionPane.showMessageDialog(Main.parent, tr("No GPX track available in layer to associate audio with."));
     948            return;
     949        }
     950
     951        // (a) try explicit timestamped waypoints - unless suppressed
     952        if (Main.pref.getBoolean("marker.audiofromexplicitwaypoints", true) &&
     953            data.waypoints != null && ! data.waypoints.isEmpty())
     954        {
     955            for (WayPoint w : data.waypoints) {
     956                if (w.time > firstTime) {
     957                    waypoints.add(w);
     958                } else if (w.time > 0.0) {
     959                    timedMarkersOmitted = true;
     960                }
     961            }
     962        }
     963
     964        // (b) try explicit waypoints without timestamps - unless suppressed
     965        if (Main.pref.getBoolean("marker.audiofromuntimedwaypoints", true) &&
     966            data.waypoints != null && ! data.waypoints.isEmpty())
     967        {
     968            for (WayPoint w : data.waypoints) {
     969                if (waypoints.contains(w)) { continue; }
     970                WayPoint wNear = nearestPointOnTrack(w.eastNorth, snapDistance);
     971                if (wNear != null) {
     972                    WayPoint wc = new WayPoint(w.latlon);
     973                    wc.time = wNear.time;
     974                    if (w.attr.containsKey("name")) wc.attr.put("name", w.getString("name"));
     975                    waypoints.add(wc);
     976                } else {
     977                    untimedMarkersOmitted = true;
     978                }
     979            }
     980        }
     981
     982        // (c) use explicitly named track points, again unless suppressed
     983        if ((Main.pref.getBoolean("marker.audiofromnamedtrackpoints", false)) &&
     984            data.tracks != null && ! data.tracks.isEmpty())
     985        {
     986            for (GpxTrack track : data.tracks) {
     987                if (track.trackSegs == null) continue;
     988                for (Collection<WayPoint> seg : track.trackSegs) {
     989                    for (WayPoint w : seg) {
     990                        if (w.attr.containsKey("name") || w.attr.containsKey("desc")) {
     991                            waypoints.add(w);
     992                        }
     993                    }
     994                }
     995            }
     996        }
     997
     998        // (d) analyse audio for spoken markers here, in due course
     999
     1000        // (e) simply add a single marker at the start of the track
     1001        if ((Main.pref.getBoolean("marker.audiofromstart") || waypoints.isEmpty()) &&
     1002            data.tracks != null && ! data.tracks.isEmpty())
     1003        {
     1004            boolean gotOne = false;
     1005            for (GpxTrack track : data.tracks) {
     1006                if (track.trackSegs == null) continue;
     1007                for (Collection<WayPoint> seg : track.trackSegs) {
     1008                    for (WayPoint w : seg) {
     1009                        WayPoint wStart = new WayPoint(w.latlon);
     1010                        wStart.attr.put("name", "start");
     1011                        wStart.time = w.time;
     1012                        waypoints.add(wStart);
     1013                        gotOne = true;
     1014                        break;
     1015                    }
     1016                    if (gotOne) break;
     1017                }
     1018                if (gotOne) break;
     1019            }
     1020        }
     1021
     1022        /* we must have got at least one waypoint now */
     1023
     1024        Collections.sort((ArrayList<WayPoint>) waypoints, new Comparator<WayPoint>() {
     1025            public int compare(WayPoint a, WayPoint b) {
     1026                return a.time <= b.time ? -1 : 1;
     1027            }
     1028        });
     1029
     1030        firstTime = -1.0; /* this time of the first waypoint, not first trackpoint */
     1031        for (WayPoint w : waypoints) {
     1032            if (firstTime < 0.0) firstTime = w.time;
     1033            double offset = w.time - firstTime;
     1034            String name;
     1035            if (w.attr.containsKey("name"))
     1036                name = w.getString("name");
     1037            else if (w.attr.containsKey("desc"))
     1038                name = w.getString("desc");
     1039            else
     1040                name = AudioMarker.inventName(offset);
     1041            AudioMarker am = AudioMarker.create(w.latlon,
     1042                    name, uri, ml, w.time, offset);
     1043            ml.data.add(am);
     1044        }
     1045        Main.main.addLayer(ml);
     1046
     1047        if (timedMarkersOmitted) {
     1048            JOptionPane.showMessageDialog(Main.parent,
     1049            tr("Some waypoints with timestamps from before the start of the track were omitted."));
     1050        }
     1051        if (untimedMarkersOmitted) {
     1052            JOptionPane.showMessageDialog(Main.parent,
     1053            tr("Some waypoints which were too far from the track to sensibly estimate their time were omitted."));
     1054        }
     1055    }
     1056
     1057    /**
     1058    * Makes a WayPoint at the projection of point P onto the track providing P is
     1059    * less than tolerance away from the track
     1060
     1061    * @param P : the point to determine the projection for
     1062    * @param tolerance : must be no further than this from the track
     1063    * @return the closest point on the track to P, which may be the
     1064    * first or last point if off the end of a segment, or may be null if
     1065    * nothing close enough
     1066    */
     1067    public WayPoint nearestPointOnTrack(EastNorth P, double tolerance) {
     1068        /*
     1069        * assume the coordinates of P are xp,yp, and those of a section of track
     1070        * between two trackpoints are R=xr,yr and S=xs,ys. Let N be the projected point.
     1071        *
     1072        * The equation of RS is Ax + By + C = 0 where
     1073        * A = ys - yr
     1074        * B = xr - xs
     1075        * C = - Axr - Byr
     1076        *
     1077        * Also, note that the distance RS^2 is A^2 + B^2
     1078        *
     1079        * If RS^2 == 0.0 ignore the degenerate section of track
     1080        *
     1081        * PN^2 = (Axp + Byp + C)^2 / RS^2
     1082        * that is the distance from P to the line
     1083        *
     1084        * so if PN^2 is less than PNmin^2 (initialized to tolerance) we can reject
     1085        * the line; otherwise...
     1086        * determine if the projected poijnt lies within the bounds of the line:
     1087        * PR^2 - PN^2 <= RS^2 and PS^2 - PN^2 <= RS^2
     1088        *
     1089        * where PR^2 = (xp - xr)^2 + (yp-yr)^2
     1090        * and   PS^2 = (xp - xs)^2 + (yp-ys)^2
     1091        *
     1092        * If so, calculate N as
     1093        * xn = xr + (RN/RS) B
     1094        * yn = y1 + (RN/RS) A
     1095        *
     1096        * where RN = sqrt(PR^2 - PN^2)
     1097        */
     1098
     1099        double PNminsq = tolerance * tolerance;
     1100        EastNorth bestEN = null;
     1101        double bestTime = 0.0;
     1102        double px = P.east();
     1103        double py = P.north();
     1104        double rx = 0.0, ry = 0.0, sx, sy, x, y;
     1105        if (data.tracks == null) return null;
     1106        for (GpxTrack track : data.tracks) {
     1107            if (track.trackSegs == null) continue;
     1108            for (Collection<WayPoint> seg : track.trackSegs) {
     1109                WayPoint R = null;
     1110                for (WayPoint S : seg) {
     1111                    if (R == null) {
     1112                        R = S;
     1113                        rx = R.eastNorth.east();
     1114                        ry = R.eastNorth.north();
     1115                        x = px - rx;
     1116                        y = py - ry;
     1117                        double PRsq = x * x + y * y;
     1118                        if (PRsq < PNminsq) {
     1119                            PNminsq = PRsq;
     1120                            bestEN = R.eastNorth;
     1121                            bestTime = R.time;
     1122                        }
     1123                    } else {
     1124                        sx = S.eastNorth.east();
     1125                        sy = S.eastNorth.north();
     1126                        double A = sy - ry;
     1127                        double B = rx - sx;
     1128                        double C = - A * rx - B * ry;
     1129                        double RSsq = A * A + B * B;
     1130                        if (RSsq == 0.0) continue;
     1131                        double PNsq = A * px + B * py + C;
     1132                        PNsq = PNsq * PNsq / RSsq;
     1133                        if (PNsq < PNminsq) {
     1134                            x = px - rx;
     1135                            y = py - ry;
     1136                            double PRsq = x * x + y * y;
     1137                            x = px - sx;
     1138                            y = py - sy;
     1139                            double PSsq = x * x + y * y;
     1140                            if (PRsq - PNsq <= RSsq && PSsq - PNsq <= RSsq) {
     1141                                double RNoverRS = Math.sqrt((PRsq - PNsq)/RSsq);
     1142                                double nx = rx - RNoverRS * B;
     1143                                double ny = ry + RNoverRS * A;
     1144                                bestEN = new EastNorth(nx, ny);
     1145                                bestTime = R.time + RNoverRS * (S.time - R.time);
     1146                                PNminsq = PNsq;
     1147                            }
     1148                        }
     1149                        R = S;
     1150                        rx = sx;
     1151                        ry = sy;
     1152                    }
     1153                }
     1154                if (R != null) {
     1155                    /* if there is only one point in the seg, it will do this twice, but no matter */
     1156                    rx = R.eastNorth.east();
     1157                    ry = R.eastNorth.north();
     1158                    x = px - rx;
     1159                    y = py - ry;
     1160                    double PRsq = x * x + y * y;
     1161                    if (PRsq < PNminsq) {
     1162                        PNminsq = PRsq;
     1163                        bestEN = R.eastNorth;
     1164                        bestTime = R.time;
     1165                    }
     1166                }
     1167            }
     1168        }
     1169        if (bestEN == null) return null;
     1170        WayPoint best = new WayPoint(Main.proj.eastNorth2latlon(bestEN));
     1171        best.time = bestTime;
     1172        return best;
     1173    }
    11741174}
  • trunk/src/org/openstreetmap/josm/gui/layer/Layer.java

    r989 r1169  
    1717/**
    1818 * A layer encapsulates the gui componente of one dataset and its representation.
    19  * 
    20  * Some layers may display data directly importet from OSM server. Other only 
    21  * display background images. Some can be edited, some not. Some are static and 
     19 *
     20 * Some layers may display data directly importet from OSM server. Other only
     21 * display background images. Some can be edited, some not. Some are static and
    2222 * other changes dynamically (auto-updated).
    2323 *
    2424 * Layers can be visible or not. Most actions the user can do applies only on
    2525 * selected layers. The available actions depend on the selected layers too.
    26  * 
    27  * All layers are managed by the MapView. They are displayed in a list to the 
     26 *
     27 * All layers are managed by the MapView. They are displayed in a list to the
    2828 * right of the screen.
    29  * 
     29 *
    3030 * @author imi
    3131 */
    3232abstract public class Layer implements Destroyable, MapViewPaintable {
    3333
    34         /**
    35         * Interface to notify listeners of the change of the active layer.
    36         * @author imi
    37         */
    38         public interface LayerChangeListener {
    39                 void activeLayerChange(Layer oldLayer, Layer newLayer);
    40                 void layerAdded(Layer newLayer);
    41                 void layerRemoved(Layer oldLayer);
    42         }
     34    /**
     35    * Interface to notify listeners of the change of the active layer.
     36    * @author imi
     37    */
     38    public interface LayerChangeListener {
     39        void activeLayerChange(Layer oldLayer, Layer newLayer);
     40        void layerAdded(Layer newLayer);
     41        void layerRemoved(Layer oldLayer);
     42    }
    4343
    44         /**
    45         * The listener of the active layer changes. You may register/deregister yourself
    46         * while an LayerChangeListener - action is executed.
    47         */
    48         public static final Collection<LayerChangeListener> listeners = new CopyOnWriteArrayList<LayerChangeListener>();
     44    /**
     45    * The listener of the active layer changes. You may register/deregister yourself
     46    * while an LayerChangeListener - action is executed.
     47    */
     48    public static final Collection<LayerChangeListener> listeners = new CopyOnWriteArrayList<LayerChangeListener>();
    4949
    50         /**
    51         * The visibility state of the layer.
    52         */
    53         public boolean visible = true;
     50    /**
     51    * The visibility state of the layer.
     52    */
     53    public boolean visible = true;
    5454
    55         /**
    56         * The layer should be handled as a background layer in automatic handling
    57         */
    58         public boolean background = false;
     55    /**
     56    * The layer should be handled as a background layer in automatic handling
     57    */
     58    public boolean background = false;
    5959
    60         /**
    61         * The name of this layer.
    62         */
    63         public String name;
    64         /**
    65         * If a file is associated with this layer, this variable should be set to it.
    66         */
    67         public File associatedFile;
     60    /**
     61    * The name of this layer.
     62    */
     63    public String name;
     64    /**
     65    * If a file is associated with this layer, this variable should be set to it.
     66    */
     67    public File associatedFile;
    6868
    69         /**
    70         * Create the layer and fill in the necessary components.
    71         */
    72         public Layer(String name) {
    73                 this.name = name;
    74         }
     69    /**
     70    * Create the layer and fill in the necessary components.
     71    */
     72    public Layer(String name) {
     73        this.name = name;
     74    }
    7575
    76         /**
    77         * Paint the dataset using the engine set.
    78         * @param mv The object that can translate GeoPoints to screen coordinates.
    79         */
    80         abstract public void paint(Graphics g, MapView mv);
    81         /**
    82          * Return a representative small image for this layer. The image must not
    83         * be larger than 64 pixel in any dimension.
    84         */
    85         abstract public Icon getIcon();
     76    /**
     77    * Paint the dataset using the engine set.
     78    * @param mv The object that can translate GeoPoints to screen coordinates.
     79    */
     80    abstract public void paint(Graphics g, MapView mv);
     81    /**
     82     * Return a representative small image for this layer. The image must not
     83    * be larger than 64 pixel in any dimension.
     84    */
     85    abstract public Icon getIcon();
    8686
    87         /**
    88         * @return A small tooltip hint about some statistics for this layer.
    89         */
    90         abstract public String getToolTipText();
     87    /**
     88    * @return A small tooltip hint about some statistics for this layer.
     89    */
     90    abstract public String getToolTipText();
    9191
    92         /**
    93          * Merges the given layer into this layer. Throws if the layer types are
    94          * incompatible.
    95          * @param from The layer that get merged into this one. After the merge,
    96          *              the other layer is not usable anymore and passing to one others
    97          *              mergeFrom should be one of the last things to do with a layer.
    98          */
    99         abstract public void mergeFrom(Layer from);
    100        
    101         /**
    102          * @param other The other layer that is tested to be mergable with this.
    103          * @return Whether the other layer can be merged into this layer.
    104          */
    105         abstract public boolean isMergable(Layer other);
     92    /**
     93     * Merges the given layer into this layer. Throws if the layer types are
     94     * incompatible.
     95     * @param from The layer that get merged into this one. After the merge,
     96     *      the other layer is not usable anymore and passing to one others
     97     *      mergeFrom should be one of the last things to do with a layer.
     98     */
     99    abstract public void mergeFrom(Layer from);
    106100
    107         abstract public void visitBoundingBox(BoundingXYVisitor v);
     101    /**
     102     * @param other The other layer that is tested to be mergable with this.
     103     * @return Whether the other layer can be merged into this layer.
     104     */
     105    abstract public boolean isMergable(Layer other);
    108106
    109         abstract public Object getInfoComponent();
    110        
    111         abstract public Component[] getMenuEntries();
    112        
    113         /**
    114          * Called, when the layer is removed from the mapview and is going to be
    115          * destroyed.
    116          *
    117          * This is because the Layer constructor can not add itself safely as listener
    118          * to the layerlist dialog, because there may be no such dialog yet (loaded
    119          * via command line parameter).
    120          */
    121         public void destroy() {}
     107    abstract public void visitBoundingBox(BoundingXYVisitor v);
     108
     109    abstract public Object getInfoComponent();
     110
     111    abstract public Component[] getMenuEntries();
     112
     113    /**
     114     * Called, when the layer is removed from the mapview and is going to be
     115     * destroyed.
     116     *
     117     * This is because the Layer constructor can not add itself safely as listener
     118     * to the layerlist dialog, because there may be no such dialog yet (loaded
     119     * via command line parameter).
     120     */
     121    public void destroy() {}
    122122}
  • trunk/src/org/openstreetmap/josm/gui/layer/MapViewPaintable.java

    r627 r1169  
    88public interface MapViewPaintable {
    99
    10         /**
    11         * Paint the dataset using the engine set.
    12         * @param mv The object that can translate GeoPoints to screen coordinates.
    13         */
    14         abstract public void paint(Graphics g, MapView mv);
     10    /**
     11    * Paint the dataset using the engine set.
     12    * @param mv The object that can translate GeoPoints to screen coordinates.
     13    */
     14    abstract public void paint(Graphics g, MapView mv);
    1515
    1616}
  • trunk/src/org/openstreetmap/josm/gui/layer/OsmDataLayer.java

    r1083 r1169  
    6868 * A layer holding data from a specific dataset.
    6969 * The data can be fully edited.
    70  * 
     70 *
    7171 * @author imi
    7272 */
     
    7474
    7575    public final static class DataCountVisitor implements Visitor {
    76         public final int[] normal = new int[3];       
     76        public final int[] normal = new int[3];
    7777        public final int[] deleted = new int[3];
    7878        public final String[] names = {"node", "way", "relation"};
     
    131131     */
    132132    private static TexturePaint hatched;
    133    
     133
    134134    static {
    135135        createHatchTexture();
     
    178178        boolean inactive = !active && Main.pref.getBoolean("draw.data.inactive_color", true);
    179179        boolean virtual = !inactive && Main.map.mapView.useVirtualNodes();
    180        
    181         // draw the hatched area for non-downloaded region. only draw if we're the active 
     180
     181        // draw the hatched area for non-downloaded region. only draw if we're the active
    182182        // and bounds are defined; don't draw for inactive layers or loaded GPX files etc
    183183        if (active && Main.pref.getBoolean("draw.data.downloaded_area", true) && !data.dataSources.isEmpty()) {
    184184            // initialize area with current viewport
    185185            Rectangle b = Main.map.mapView.getBounds();
    186             // on some platforms viewport bounds seem to be offset from the left, 
     186            // on some platforms viewport bounds seem to be offset from the left,
    187187            // over-grow it just to be sure
    188188            b.grow(100, 100);
    189189            Area a = new Area(b);
    190            
     190
    191191            // now succesively subtract downloaded areas
    192192            for (DataSource src : data.dataSources) {
     
    200200                }
    201201            }
    202            
     202
    203203            // paint remainder
    204204            ((Graphics2D)g).setPaint(hatched);
    205205            ((Graphics2D)g).fill(a);
    206206        }
    207    
     207
    208208        SimplePaintVisitor painter;
    209209        if (Main.pref.getBoolean("draw.wireframe"))
     
    234234
    235235        // copy the merged layer's data source info
    236         for (DataSource src : ((OsmDataLayer)from).data.dataSources) 
     236        for (DataSource src : ((OsmDataLayer)from).data.dataSources)
    237237            data.dataSources.add(src);
    238238        fireDataChange();
    239239        // repaint to make sure new data is displayed properly.
    240240        Main.map.mapView.repaint();
    241        
     241
    242242        if (visitor.conflicts.isEmpty())
    243243            return;
     
    263263     * really deleting all deleted objects and reset the modified flags. This is done
    264264     * after a successfull upload.
    265      * 
    266      * @param processed A list of all objects that were actually uploaded. 
    267      *         May be <code>null</code>, which means nothing has been uploaded but 
     265     *
     266     * @param processed A list of all objects that were actually uploaded.
     267     *         May be <code>null</code>, which means nothing has been uploaded but
    268268     *         saved to disk instead. Note that an empty collection for "processed"
    269269     *      means that an upload has been attempted but failed.
     
    274274        if (processed != null && processed.isEmpty() && !dataAdded)
    275275            return;
    276        
     276
    277277        Main.main.undoRedo.clean();
    278278
     
    300300     * Clean the modified flag for the given iterator over a collection if it is in the
    301301     * list of processed entries.
    302      * 
     302     *
    303303     * @param it The iterator to change the modified and remove the items if deleted.
    304304     * @param processed A list of all objects that have been successfully progressed.
     
    415415            }
    416416        }
    417        
    418         // what is this loop meant to do? it creates waypoints but never 
     417
     418        // what is this loop meant to do? it creates waypoints but never
    419419        // records them?
    420420        for (Node n : data.nodes) {
  • trunk/src/org/openstreetmap/josm/gui/layer/RawGpsLayer.java

    r999 r1169  
    5757 * A layer holding data from a gps source.
    5858 * The data is read only.
    59  * 
     59 *
    6060 * @author imi
    6161 */
    6262public class RawGpsLayer extends Layer implements PreferenceChangedListener {
    6363
    64         public class ConvertToDataLayerAction extends AbstractAction {
    65                 public ConvertToDataLayerAction() {
    66                         super(tr("Convert to data layer"), ImageProvider.get("converttoosm"));
    67                 }
    68                 public void actionPerformed(ActionEvent e) {
    69                         JPanel msg = new JPanel(new GridBagLayout());
    70                         msg.add(new JLabel(tr("<html>Upload of unprocessed GPS data as map data is considered harmful.<br>If you want to upload traces, look here:")), GBC.eol());
    71                         msg.add(new UrlLabel(tr("http://www.openstreetmap.org/traces")), GBC.eop());
    72                         if (!DontShowAgainInfo.show("convert_to_data", msg))
    73                                 return;
    74                         DataSet ds = new DataSet();
    75                         for (Collection<GpsPoint> c : data) {
    76                                 Way w = new Way();
    77                                 for (GpsPoint p : c) {
    78                                         Node n = new Node(p.latlon);
    79                                         ds.nodes.add(n);
    80                                         w.nodes.add(n);
    81                                 }
    82                                 ds.ways.add(w);
    83                         }
    84                         Main.main.addLayer(new OsmDataLayer(ds, tr("Converted from: {0}", RawGpsLayer.this.name), null));
    85                         Main.main.removeLayer(RawGpsLayer.this);
    86                 }
    87         }
    88        
    89         public class UploadTraceAction extends AbstractAction {
    90                 public UploadTraceAction() {
    91                         super(tr("Upload this trace..."), ImageProvider.get("uploadtrace"));
    92                 }
    93                 public void actionPerformed(ActionEvent e) {
    94                         JPanel msg = new JPanel(new GridBagLayout());
    95                         msg.add(new JLabel(tr("<html>This functionality has been added only recently. Please<br>"+
    96                                                       "use with care and check if it works as expected.</html>")), GBC.eop());
    97                         ButtonGroup bg = new ButtonGroup();
    98                         JRadioButton c1 = null;
    99                         JRadioButton c2 = null;
    100                        
    101                         if (associatedFile != null) {
    102                                 c1 = new JRadioButton(tr("Upload track filtered by JOSM"), false);
    103                                 c2 = new JRadioButton(tr("Upload raw file: {0}", associatedFile.getName()), true);
    104                         }
    105                         else
    106                         {
    107                                 c1 = new JRadioButton(tr("Upload track filtered by JOSM"), true);
    108                                 c2 = new JRadioButton(tr("Upload raw file: "), false);
    109                                 c2.setEnabled(false);
    110                         }
    111                         c1.setEnabled(false);
    112                         bg.add(c1);
    113                         bg.add(c2);
    114 
    115                         msg.add(c1, GBC.eol());
    116                         msg.add(c2, GBC.eop());
    117 
    118                        
    119                         JTextField description = new JTextField();
    120                         JTextField tags = new JTextField();
    121                         msg.add(new JLabel(tr("Description:")), GBC.std());
    122                         msg.add(description, GBC.eol().fill(GBC.HORIZONTAL));
    123                         msg.add(new JLabel(tr("Tags:")), GBC.std());
    124                         msg.add(tags, GBC.eol().fill(GBC.HORIZONTAL));
    125                         JCheckBox c3 = new JCheckBox("public");
    126                         msg.add(c3, GBC.eop());
    127                         msg.add(new JLabel("Please ensure that you don't upload your traces twice."), GBC.eop());
    128                        
    129                         int answer = JOptionPane.showConfirmDialog(Main.parent, msg, tr("GPX-Upload"), JOptionPane.OK_CANCEL_OPTION);
    130                         if (answer == JOptionPane.OK_OPTION)
    131                         {
    132                                 try {
    133                                         String version = Main.pref.get("osm-server.version", "0.5");
    134                                         URL url = new URL(Main.pref.get("osm-server.url") +
    135                                                         "/" + version + "/gpx/create");
    136 
    137                                         // create a boundary string
    138                                         String boundary = MultiPartFormOutputStream.createBoundary();
    139                                         URLConnection urlConn = MultiPartFormOutputStream.createConnection(url);
    140                                         urlConn.setRequestProperty("Accept", "*/*");
    141                                         urlConn.setRequestProperty("Content-Type",
    142                                                 MultiPartFormOutputStream.getContentType(boundary));
    143                                         // set some other request headers...
    144                                         urlConn.setRequestProperty("Connection", "Keep-Alive");
    145                                         urlConn.setRequestProperty("Cache-Control", "no-cache");
    146                                         // no need to connect cuz getOutputStream() does it
    147                                         MultiPartFormOutputStream out =
    148                                                 new MultiPartFormOutputStream(urlConn.getOutputStream(), boundary);
    149                                         out.writeField("description", description.getText());
    150                                         out.writeField("tags", tags.getText());
    151                                         out.writeField("public", (c3.getSelectedObjects() != null) ? "1" : "0");
    152                                         // upload a file
    153                                         out.writeFile("gpx_file", "text/xml", associatedFile);
    154                                         // can also write bytes directly
    155                                         // out.writeFile("myFile", "text/plain", "C:\\test.txt",
    156                                         // "This is some file text.".getBytes("ASCII"));
    157                                         out.close();
    158                                         // read response from server
    159                                         BufferedReader in = new BufferedReader(
    160                                                 new InputStreamReader(urlConn.getInputStream()));
    161                                         String line = "";
    162                                         while((line = in.readLine()) != null) {
    163                                                 System.out.println(line);
    164                                         }
    165                                         in.close();
    166                                        
    167                                         /*
    168                                         int retCode = activeConnection.getResponseCode();
    169                                         System.out.println("got return: "+retCode);
    170                                         String retMsg = activeConnection.getResponseMessage();
    171                                         activeConnection.disconnect();
    172                                         if (retCode != 200) {
    173                                                 // Look for a detailed error message from the server
    174                                                 if (activeConnection.getHeaderField("Error") != null)
    175                                                         retMsg += "\n" + activeConnection.getHeaderField("Error");
    176 
    177                                                 // Report our error
    178                                                 ByteArrayOutputStream o = new ByteArrayOutputStream();
    179                                                 System.out.println(new String(o.toByteArray(), "UTF-8").toString());
    180                                                 throw new RuntimeException(retCode+" "+retMsg);
    181                                         }
    182                                         */
    183                                 } catch (UnknownHostException ex) {
    184                                         throw new RuntimeException(tr("Unknown host")+": "+ex.getMessage(), ex);
    185                                 } catch (Exception ex) {
    186                                         //if (cancel)
    187                                         //      return; // assume cancel
    188                                         if (ex instanceof RuntimeException)
    189                                                 throw (RuntimeException)ex;
    190                                         throw new RuntimeException(ex.getMessage(), ex);
    191                                 }       
    192                         }
    193                 }
    194         }
    195 
    196         public static class GpsPoint {
    197                 public final LatLon latlon;
    198                 public final EastNorth eastNorth;
    199                 public final String time;
    200                 public GpsPoint(LatLon ll, String t) {
    201                         latlon = ll;
    202                         eastNorth = Main.proj.latlon2eastNorth(ll);
    203                         time = t;
    204                 }
    205         }
    206 
    207         /**
    208         * A list of ways which containing a list of points.
    209         */
    210         public final Collection<Collection<GpsPoint>> data;
    211         public final boolean fromServer;
    212 
    213         public RawGpsLayer(boolean fromServer, Collection<Collection<GpsPoint>> data, String name, File associatedFile) {
    214                 super(name);
    215                 this.fromServer = fromServer;
    216                 this.associatedFile = associatedFile;
    217                 this.data = data;
    218                 Main.pref.listener.add(this);
    219         }
    220 
    221         /**
    222         * Return a static icon.
    223         */
    224         @Override public Icon getIcon() {
    225                 return ImageProvider.get("layer", "rawgps_small");
    226         }
    227 
    228         @Override public void paint(Graphics g, MapView mv) {
    229                 g.setColor(Main.pref.getColor(marktr("gps point"), "layer "+name, Color.gray));
    230                 Point old = null;
    231 
    232                 boolean force = Main.pref.getBoolean("draw.rawgps.lines.force");
    233                 boolean lines = Main.pref.getBoolean("draw.rawgps.lines");
    234                 String linesKey = "draw.rawgps.lines.layer "+name;
    235                 if (Main.pref.hasKey(linesKey))
    236                         lines = Main.pref.getBoolean(linesKey);
    237                 boolean large = Main.pref.getBoolean("draw.rawgps.large");
    238                 for (Collection<GpsPoint> c : data) {
    239                         if (!force)
    240                                 old = null;
    241                         for (GpsPoint p : c) {
    242                                 Point screen = mv.getPoint(p.eastNorth);
    243                                 if (lines && old != null)
    244                                         g.drawLine(old.x, old.y, screen.x, screen.y);
    245                                 else if (!large)
    246                                         g.drawRect(screen.x, screen.y, 0, 0);
    247                                 if (large)
    248                                         g.fillRect(screen.x-1, screen.y-1, 3, 3);
    249                                 old = screen;
    250                         }
    251                 }
    252         }
    253 
    254         @Override public String getToolTipText() {
    255                 int points = 0;
    256                 for (Collection<GpsPoint> c : data)
    257                         points += c.size();
    258                 String tool = data.size()+" "+trn("track", "tracks", data.size())
    259                 +" "+points+" "+trn("point", "points", points);
    260                 if (associatedFile != null)
    261                         tool = "<html>"+tool+"<br>"+associatedFile.getPath()+"</html>";
    262                 return tool;
    263         }
    264 
    265         @Override public void mergeFrom(Layer from) {
    266                 RawGpsLayer layer = (RawGpsLayer)from;
    267                 data.addAll(layer.data);
    268         }
    269 
    270         @Override public boolean isMergable(Layer other) {
    271                 return other instanceof RawGpsLayer;
    272         }
    273 
    274         @Override public void visitBoundingBox(BoundingXYVisitor v) {
    275                 for (Collection<GpsPoint> c : data)
    276                         for (GpsPoint p : c)
    277                                 v.visit(p.eastNorth);
    278         }
    279 
    280         @Override public Object getInfoComponent() {
    281                 StringBuilder b = new StringBuilder();
    282                 int points = 0;
    283                 for (Collection<GpsPoint> c : data) {
    284                         b.append("&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"+trn("a track with {0} point","a track with {0} points", c.size(), c.size())+"<br>");
    285                         points += c.size();
    286                 }
    287                 b.append("</html>");
    288                 return "<html>"+trn("{0} consists of {1} track", "{0} consists of {1} tracks", data.size(), name, data.size())+" ("+trn("{0} point", "{0} points", points, points)+")<br>"+b.toString();
    289         }
    290 
    291         @Override public Component[] getMenuEntries() {
    292                 JMenuItem line = new JMenuItem(tr("Customize line drawing"), ImageProvider.get("mapmode/addsegment"));
    293                 line.addActionListener(new ActionListener(){
    294                         public void actionPerformed(ActionEvent e) {
    295                                 JRadioButton[] r = new JRadioButton[3];
    296                                 r[0] = new JRadioButton(tr("Use global settings."));
    297                                 r[1] = new JRadioButton(tr("Draw lines between points for this layer."));
    298                                 r[2] = new JRadioButton(tr("Do not draw lines between points for this layer."));
    299                                 ButtonGroup group = new ButtonGroup();
    300                                 Box panel = Box.createVerticalBox();
    301                                 for (JRadioButton b : r) {
    302                                         group.add(b);
    303                                         panel.add(b);
    304                                 }
    305                                 String propName = "draw.rawgps.lines.layer "+name;
    306                                 if (Main.pref.hasKey(propName))
    307                                         group.setSelected(r[Main.pref.getBoolean(propName) ? 1:2].getModel(), true);
    308                                 else
    309                                         group.setSelected(r[0].getModel(), true);
    310                                 int answer = JOptionPane.showConfirmDialog(Main.parent, panel, tr("Select line drawing options"), JOptionPane.OK_CANCEL_OPTION);
    311                                 if (answer == JOptionPane.CANCEL_OPTION)
    312                                         return;
    313                                 if (group.getSelection() == r[0].getModel())
    314                                         Main.pref.put(propName, null);
    315                                 else
    316                                         Main.pref.put(propName, group.getSelection() == r[1].getModel());
    317                                 Main.map.repaint();
    318                         }
    319                 });
    320 
    321                 JMenuItem color = new JMenuItem(tr("Customize Color"), ImageProvider.get("colorchooser"));
    322                 color.addActionListener(new ActionListener(){
    323                         public void actionPerformed(ActionEvent e) {
    324                                 JColorChooser c = new JColorChooser(Main.pref.getColor(marktr("gps point"), "layer "+name, Color.gray));
    325                                 Object[] options = new Object[]{tr("OK"), tr("Cancel"), tr("Default")};
    326                                 int answer = JOptionPane.showOptionDialog(Main.parent, c, tr("Choose a color"), JOptionPane.OK_CANCEL_OPTION,
    327                                 JOptionPane.PLAIN_MESSAGE, null, options, options[0]);
    328                                 switch (answer) {
    329                                 case 0:
    330                                         Main.pref.putColor("layer "+name, c.getColor());
    331                                         break;
    332                                 case 1:
    333                                         return;
    334                                 case 2:
    335                                         Main.pref.putColor("layer "+name, null);
    336                                         break;
    337                                 }
    338                                 Main.map.repaint();
    339                         }
    340                 });
    341 
    342                 if (Main.applet)
    343                         return new Component[]{
    344                                 new JMenuItem(new LayerListDialog.ShowHideLayerAction(this)),
    345                                 new JMenuItem(new LayerListDialog.DeleteLayerAction(this)),
    346                                 new JSeparator(),
    347                                 color,
    348                                 line,
    349                                 new JMenuItem(new ConvertToDataLayerAction()),
    350                                 //new JMenuItem(new UploadTraceAction()),
    351                                 new JSeparator(),
    352                                 new JMenuItem(new RenameLayerAction(associatedFile, this)),
    353                                 new JSeparator(),
    354                                 new JMenuItem(new LayerListPopup.InfoAction(this))};
    355                 return new Component[]{
    356                                 new JMenuItem(new LayerListDialog.ShowHideLayerAction(this)),
    357                                 new JMenuItem(new LayerListDialog.DeleteLayerAction(this)),
    358                                 new JSeparator(),
    359                                 new JMenuItem(new GpxExportAction(this)),
    360                                 color,
    361                                 line,
    362                                 new JMenuItem(new ConvertToDataLayerAction()),
    363                                 //new JMenuItem(new UploadTraceAction()),
    364                                 new JSeparator(),
    365                                 new JMenuItem(new RenameLayerAction(associatedFile, this)),
    366                                 new JSeparator(),
    367                                 new JMenuItem(new LayerListPopup.InfoAction(this))};
    368         }
    369 
    370         public void preferenceChanged(String key, String newValue) {
    371                 if (Main.map != null && (key.equals("draw.rawgps.lines") || key.equals("draw.rawgps.lines.force")))
    372                         Main.map.repaint();
    373         }
    374 
    375         @Override public void destroy() {
    376                 Main.pref.listener.remove(RawGpsLayer.this);
     64    public class ConvertToDataLayerAction extends AbstractAction {
     65        public ConvertToDataLayerAction() {
     66            super(tr("Convert to data layer"), ImageProvider.get("converttoosm"));
     67        }
     68        public void actionPerformed(ActionEvent e) {
     69            JPanel msg = new JPanel(new GridBagLayout());
     70            msg.add(new JLabel(tr("<html>Upload of unprocessed GPS data as map data is considered harmful.<br>If you want to upload traces, look here:")), GBC.eol());
     71            msg.add(new UrlLabel(tr("http://www.openstreetmap.org/traces")), GBC.eop());
     72            if (!DontShowAgainInfo.show("convert_to_data", msg))
     73                return;
     74            DataSet ds = new DataSet();
     75            for (Collection<GpsPoint> c : data) {
     76                Way w = new Way();
     77                for (GpsPoint p : c) {
     78                    Node n = new Node(p.latlon);
     79                    ds.nodes.add(n);
     80                    w.nodes.add(n);
     81                }
     82                ds.ways.add(w);
     83            }
     84            Main.main.addLayer(new OsmDataLayer(ds, tr("Converted from: {0}", RawGpsLayer.this.name), null));
     85            Main.main.removeLayer(RawGpsLayer.this);
     86        }
     87    }
     88
     89    public class UploadTraceAction extends AbstractAction {
     90        public UploadTraceAction() {
     91            super(tr("Upload this trace..."), ImageProvider.get("uploadtrace"));
     92        }
     93        public void actionPerformed(ActionEvent e) {
     94            JPanel msg = new JPanel(new GridBagLayout());
     95            msg.add(new JLabel(tr("<html>This functionality has been added only recently. Please<br>"+
     96                                  "use with care and check if it works as expected.</html>")), GBC.eop());
     97            ButtonGroup bg = new ButtonGroup();
     98            JRadioButton c1 = null;
     99            JRadioButton c2 = null;
     100
     101            if (associatedFile != null) {
     102                c1 = new JRadioButton(tr("Upload track filtered by JOSM"), false);
     103                c2 = new JRadioButton(tr("Upload raw file: {0}", associatedFile.getName()), true);
     104            }
     105            else
     106            {
     107                c1 = new JRadioButton(tr("Upload track filtered by JOSM"), true);
     108                c2 = new JRadioButton(tr("Upload raw file: "), false);
     109                c2.setEnabled(false);
     110            }
     111            c1.setEnabled(false);
     112            bg.add(c1);
     113            bg.add(c2);
     114
     115            msg.add(c1, GBC.eol());
     116            msg.add(c2, GBC.eop());
     117
     118
     119            JTextField description = new JTextField();
     120            JTextField tags = new JTextField();
     121            msg.add(new JLabel(tr("Description:")), GBC.std());
     122            msg.add(description, GBC.eol().fill(GBC.HORIZONTAL));
     123            msg.add(new JLabel(tr("Tags:")), GBC.std());
     124            msg.add(tags, GBC.eol().fill(GBC.HORIZONTAL));
     125            JCheckBox c3 = new JCheckBox("public");
     126            msg.add(c3, GBC.eop());
     127            msg.add(new JLabel("Please ensure that you don't upload your traces twice."), GBC.eop());
     128
     129            int answer = JOptionPane.showConfirmDialog(Main.parent, msg, tr("GPX-Upload"), JOptionPane.OK_CANCEL_OPTION);
     130            if (answer == JOptionPane.OK_OPTION)
     131            {
     132                try {
     133                    String version = Main.pref.get("osm-server.version", "0.5");
     134                    URL url = new URL(Main.pref.get("osm-server.url") +
     135                            "/" + version + "/gpx/create");
     136
     137                    // create a boundary string
     138                    String boundary = MultiPartFormOutputStream.createBoundary();
     139                    URLConnection urlConn = MultiPartFormOutputStream.createConnection(url);
     140                    urlConn.setRequestProperty("Accept", "*/*");
     141                    urlConn.setRequestProperty("Content-Type",
     142                        MultiPartFormOutputStream.getContentType(boundary));
     143                    // set some other request headers...
     144                    urlConn.setRequestProperty("Connection", "Keep-Alive");
     145                    urlConn.setRequestProperty("Cache-Control", "no-cache");
     146                    // no need to connect cuz getOutputStream() does it
     147                    MultiPartFormOutputStream out =
     148                        new MultiPartFormOutputStream(urlConn.getOutputStream(), boundary);
     149                    out.writeField("description", description.getText());
     150                    out.writeField("tags", tags.getText());
     151                    out.writeField("public", (c3.getSelectedObjects() != null) ? "1" : "0");
     152                    // upload a file
     153                    out.writeFile("gpx_file", "text/xml", associatedFile);
     154                    // can also write bytes directly
     155                    // out.writeFile("myFile", "text/plain", "C:\\test.txt",
     156                    // "This is some file text.".getBytes("ASCII"));
     157                    out.close();
     158                    // read response from server
     159                    BufferedReader in = new BufferedReader(
     160                        new InputStreamReader(urlConn.getInputStream()));
     161                    String line = "";
     162                    while((line = in.readLine()) != null) {
     163                        System.out.println(line);
     164                    }
     165                    in.close();
     166
     167                    /*
     168                    int retCode = activeConnection.getResponseCode();
     169                    System.out.println("got return: "+retCode);
     170                    String retMsg = activeConnection.getResponseMessage();
     171                    activeConnection.disconnect();
     172                    if (retCode != 200) {
     173                        // Look for a detailed error message from the server
     174                        if (activeConnection.getHeaderField("Error") != null)
     175                            retMsg += "\n" + activeConnection.getHeaderField("Error");
     176
     177                        // Report our error
     178                        ByteArrayOutputStream o = new ByteArrayOutputStream();
     179                        System.out.println(new String(o.toByteArray(), "UTF-8").toString());
     180                        throw new RuntimeException(retCode+" "+retMsg);
     181                    }
     182                    */
     183                } catch (UnknownHostException ex) {
     184                    throw new RuntimeException(tr("Unknown host")+": "+ex.getMessage(), ex);
     185                } catch (Exception ex) {
     186                    //if (cancel)
     187                    //  return; // assume cancel
     188                    if (ex instanceof RuntimeException)
     189                        throw (RuntimeException)ex;
     190                    throw new RuntimeException(ex.getMessage(), ex);
     191                }
     192            }
     193        }
     194    }
     195
     196    public static class GpsPoint {
     197        public final LatLon latlon;
     198        public final EastNorth eastNorth;
     199        public final String time;
     200        public GpsPoint(LatLon ll, String t) {
     201            latlon = ll;
     202            eastNorth = Main.proj.latlon2eastNorth(ll);
     203            time = t;
     204        }
     205    }
     206
     207    /**
     208    * A list of ways which containing a list of points.
     209    */
     210    public final Collection<Collection<GpsPoint>> data;
     211    public final boolean fromServer;
     212
     213    public RawGpsLayer(boolean fromServer, Collection<Collection<GpsPoint>> data, String name, File associatedFile) {
     214        super(name);
     215        this.fromServer = fromServer;
     216        this.associatedFile = associatedFile;
     217        this.data = data;
     218        Main.pref.listener.add(this);
     219    }
     220
     221    /**
     222    * Return a static icon.
     223    */
     224    @Override public Icon getIcon() {
     225        return ImageProvider.get("layer", "rawgps_small");
     226    }
     227
     228    @Override public void paint(Graphics g, MapView mv) {
     229        g.setColor(Main.pref.getColor(marktr("gps point"), "layer "+name, Color.gray));
     230        Point old = null;
     231
     232        boolean force = Main.pref.getBoolean("draw.rawgps.lines.force");
     233        boolean lines = Main.pref.getBoolean("draw.rawgps.lines");
     234        String linesKey = "draw.rawgps.lines.layer "+name;
     235        if (Main.pref.hasKey(linesKey))
     236            lines = Main.pref.getBoolean(linesKey);
     237        boolean large = Main.pref.getBoolean("draw.rawgps.large");
     238        for (Collection<GpsPoint> c : data) {
     239            if (!force)
     240                old = null;
     241            for (GpsPoint p : c) {
     242                Point screen = mv.getPoint(p.eastNorth);
     243                if (lines && old != null)
     244                    g.drawLine(old.x, old.y, screen.x, screen.y);
     245                else if (!large)
     246                    g.drawRect(screen.x, screen.y, 0, 0);
     247                if (large)
     248                    g.fillRect(screen.x-1, screen.y-1, 3, 3);
     249                old = screen;
     250            }
     251        }
     252    }
     253
     254    @Override public String getToolTipText() {
     255        int points = 0;
     256        for (Collection<GpsPoint> c : data)
     257            points += c.size();
     258        String tool = data.size()+" "+trn("track", "tracks", data.size())
     259        +" "+points+" "+trn("point", "points", points);
     260        if (associatedFile != null)
     261            tool = "<html>"+tool+"<br>"+associatedFile.getPath()+"</html>";
     262        return tool;
     263    }
     264
     265    @Override public void mergeFrom(Layer from) {
     266        RawGpsLayer layer = (RawGpsLayer)from;
     267        data.addAll(layer.data);
     268    }
     269
     270    @Override public boolean isMergable(Layer other) {
     271        return other instanceof RawGpsLayer;
     272    }
     273
     274    @Override public void visitBoundingBox(BoundingXYVisitor v) {
     275        for (Collection<GpsPoint> c : data)
     276            for (GpsPoint p : c)
     277                v.visit(p.eastNorth);
     278    }
     279
     280    @Override public Object getInfoComponent() {
     281        StringBuilder b = new StringBuilder();
     282        int points = 0;
     283        for (Collection<GpsPoint> c : data) {
     284            b.append("&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"+trn("a track with {0} point","a track with {0} points", c.size(), c.size())+"<br>");
     285            points += c.size();
     286        }
     287        b.append("</html>");
     288        return "<html>"+trn("{0} consists of {1} track", "{0} consists of {1} tracks", data.size(), name, data.size())+" ("+trn("{0} point", "{0} points", points, points)+")<br>"+b.toString();
     289    }
     290
     291    @Override public Component[] getMenuEntries() {
     292        JMenuItem line = new JMenuItem(tr("Customize line drawing"), ImageProvider.get("mapmode/addsegment"));
     293        line.addActionListener(new ActionListener(){
     294            public void actionPerformed(ActionEvent e) {
     295                JRadioButton[] r = new JRadioButton[3];
     296                r[0] = new JRadioButton(tr("Use global settings."));
     297                r[1] = new JRadioButton(tr("Draw lines between points for this layer."));
     298                r[2] = new JRadioButton(tr("Do not draw lines between points for this layer."));
     299                ButtonGroup group = new ButtonGroup();
     300                Box panel = Box.createVerticalBox();
     301                for (JRadioButton b : r) {
     302                    group.add(b);
     303                    panel.add(b);
     304                }
     305                String propName = "draw.rawgps.lines.layer "+name;
     306                if (Main.pref.hasKey(propName))
     307                    group.setSelected(r[Main.pref.getBoolean(propName) ? 1:2].getModel(), true);
     308                else
     309                    group.setSelected(r[0].getModel(), true);
     310                int answer = JOptionPane.showConfirmDialog(Main.parent, panel, tr("Select line drawing options"), JOptionPane.OK_CANCEL_OPTION);
     311                if (answer == JOptionPane.CANCEL_OPTION)
     312                    return;
     313                if (group.getSelection() == r[0].getModel())
     314                    Main.pref.put(propName, null);
     315                else
     316                    Main.pref.put(propName, group.getSelection() == r[1].getModel());
     317                Main.map.repaint();
     318            }
     319        });
     320
     321        JMenuItem color = new JMenuItem(tr("Customize Color"), ImageProvider.get("colorchooser"));
     322        color.addActionListener(new ActionListener(){
     323            public void actionPerformed(ActionEvent e) {
     324                JColorChooser c = new JColorChooser(Main.pref.getColor(marktr("gps point"), "layer "+name, Color.gray));
     325                Object[] options = new Object[]{tr("OK"), tr("Cancel"), tr("Default")};
     326                int answer = JOptionPane.showOptionDialog(Main.parent, c, tr("Choose a color"), JOptionPane.OK_CANCEL_OPTION,
     327                JOptionPane.PLAIN_MESSAGE, null, options, options[0]);
     328                switch (answer) {
     329                case 0:
     330                    Main.pref.putColor("layer "+name, c.getColor());
     331                    break;
     332                case 1:
     333                    return;
     334                case 2:
     335                    Main.pref.putColor("layer "+name, null);
     336                    break;
     337                }
     338                Main.map.repaint();
     339            }
     340        });
     341
     342        if (Main.applet)
     343            return new Component[]{
     344                new JMenuItem(new LayerListDialog.ShowHideLayerAction(this)),
     345                new JMenuItem(new LayerListDialog.DeleteLayerAction(this)),
     346                new JSeparator(),
     347                color,
     348                line,
     349                new JMenuItem(new ConvertToDataLayerAction()),
     350                //new JMenuItem(new UploadTraceAction()),
     351                new JSeparator(),
     352                new JMenuItem(new RenameLayerAction(associatedFile, this)),
     353                new JSeparator(),
     354                new JMenuItem(new LayerListPopup.InfoAction(this))};
     355        return new Component[]{
     356                new JMenuItem(new LayerListDialog.ShowHideLayerAction(this)),
     357                new JMenuItem(new LayerListDialog.DeleteLayerAction(this)),
     358                new JSeparator(),
     359                new JMenuItem(new GpxExportAction(this)),
     360                color,
     361                line,
     362                new JMenuItem(new ConvertToDataLayerAction()),
     363                //new JMenuItem(new UploadTraceAction()),
     364                new JSeparator(),
     365                new JMenuItem(new RenameLayerAction(associatedFile, this)),
     366                new JSeparator(),
     367                new JMenuItem(new LayerListPopup.InfoAction(this))};
     368    }
     369
     370    public void preferenceChanged(String key, String newValue) {
     371        if (Main.map != null && (key.equals("draw.rawgps.lines") || key.equals("draw.rawgps.lines.force")))
     372            Main.map.repaint();
     373    }
     374
     375    @Override public void destroy() {
     376        Main.pref.listener.remove(RawGpsLayer.this);
    377377    }
    378378}
  • trunk/src/org/openstreetmap/josm/gui/layer/markerlayer/AudioMarker.java

    r627 r1169  
    1111/**
    1212 * Marker class with audio playback capability.
    13  * 
     13 *
    1414 * @author Frederik Ramm <frederik@remote.org>
    1515 *
     
    1717public class AudioMarker extends ButtonMarker {
    1818
    19         private URL audioUrl;   
    20         private static AudioMarker recentlyPlayedMarker = null;
    21         public  double syncOffset;
    22        
    23         /**
    24          * Verifies the parameter whether a new AudioMarker can be created and return
    25          * one or return <code>null</code>.
    26          */
    27         public static AudioMarker create(LatLon ll, String text, String url, MarkerLayer parentLayer, double time, double offset) {
    28                 try {
    29                         return new AudioMarker(ll, text, new URL(url), parentLayer, time, offset);
    30                 } catch (Exception ex) {
    31                         return null;
    32                 }
    33         }
     19    private URL audioUrl;
     20    private static AudioMarker recentlyPlayedMarker = null;
     21    public  double syncOffset;
    3422
    35         private AudioMarker(LatLon ll, String text, URL audioUrl, MarkerLayer parentLayer, double time, double offset) {
    36                 super(ll, text, "speech.png", parentLayer, time, offset);
    37                 this.audioUrl = audioUrl;
    38                 this.syncOffset = 0.0;
    39         }
     23    /**
     24     * Verifies the parameter whether a new AudioMarker can be created and return
     25     * one or return <code>null</code>.
     26     */
     27    public static AudioMarker create(LatLon ll, String text, String url, MarkerLayer parentLayer, double time, double offset) {
     28        try {
     29            return new AudioMarker(ll, text, new URL(url), parentLayer, time, offset);
     30        } catch (Exception ex) {
     31            return null;
     32        }
     33    }
    4034
    41         @Override public void actionPerformed(ActionEvent ev) {
    42                 play();
    43         }
     35    private AudioMarker(LatLon ll, String text, URL audioUrl, MarkerLayer parentLayer, double time, double offset) {
     36        super(ll, text, "speech.png", parentLayer, time, offset);
     37        this.audioUrl = audioUrl;
     38        this.syncOffset = 0.0;
     39    }
    4440
    45         public static AudioMarker recentlyPlayedMarker() {
    46                 return recentlyPlayedMarker;
    47         }
    48        
    49         public URL url() {
    50                 return audioUrl;
    51         }
    52                
    53         /**
    54          * Starts playing the audio associated with the marker offset by the given amount
    55          * @param after : seconds after marker where playing should start
    56          */
    57         public void play(double after) {
    58                 try {
    59                         // first enable tracing the audio along the track
    60                         Main.map.mapView.playHeadMarker.animate();
     41    @Override public void actionPerformed(ActionEvent ev) {
     42        play();
     43    }
    6144
    62                         AudioPlayer.play(audioUrl, offset + syncOffset + after);
    63                         recentlyPlayedMarker = this;
    64                 } catch (Exception e) {
    65                         AudioPlayer.audioMalfunction(e);
    66                 }
    67         }
     45    public static AudioMarker recentlyPlayedMarker() {
     46        return recentlyPlayedMarker;
     47    }
    6848
    69         /**
    70          * Starts playing the audio associated with the marker: used in response to pressing
    71          * the marker as well as indirectly
    72          *
    73          */
    74         public void play() { play(0.0); }
     49    public URL url() {
     50        return audioUrl;
     51    }
    7552
    76         public void adjustOffset(double adjustment) {
    77                 syncOffset = adjustment; // added to offset may turn out negative, but that's ok
    78         }
     53    /**
     54     * Starts playing the audio associated with the marker offset by the given amount
     55     * @param after : seconds after marker where playing should start
     56     */
     57    public void play(double after) {
     58        try {
     59            // first enable tracing the audio along the track
     60            Main.map.mapView.playHeadMarker.animate();
    7961
    80         public double syncOffset() {
    81                 return syncOffset;
    82         }
    83        
    84         public static String inventName (double offset) {
    85                 int wholeSeconds = (int)(offset + 0.5);
    86                 if (wholeSeconds < 60)
    87                         return Integer.toString(wholeSeconds);
    88                 else if (wholeSeconds < 3600)
    89                         return String.format("%d:%02d", wholeSeconds / 60, wholeSeconds % 60);
    90                 else
    91                         return String.format("%d:%02d:%02d", wholeSeconds / 3600, (wholeSeconds % 3600)/60, wholeSeconds % 60);
    92         }
     62            AudioPlayer.play(audioUrl, offset + syncOffset + after);
     63            recentlyPlayedMarker = this;
     64        } catch (Exception e) {
     65            AudioPlayer.audioMalfunction(e);
     66        }
     67    }
     68
     69    /**
     70     * Starts playing the audio associated with the marker: used in response to pressing
     71     * the marker as well as indirectly
     72     *
     73     */
     74    public void play() { play(0.0); }
     75
     76    public void adjustOffset(double adjustment) {
     77        syncOffset = adjustment; // added to offset may turn out negative, but that's ok
     78    }
     79
     80    public double syncOffset() {
     81        return syncOffset;
     82    }
     83
     84    public static String inventName (double offset) {
     85        int wholeSeconds = (int)(offset + 0.5);
     86        if (wholeSeconds < 60)
     87            return Integer.toString(wholeSeconds);
     88        else if (wholeSeconds < 3600)
     89            return String.format("%d:%02d", wholeSeconds / 60, wholeSeconds % 60);
     90        else
     91            return String.format("%d:%02d:%02d", wholeSeconds / 3600, (wholeSeconds % 3600)/60, wholeSeconds % 60);
     92    }
    9393}
  • trunk/src/org/openstreetmap/josm/gui/layer/markerlayer/ButtonMarker.java

    r627 r1169  
    1717/**
    1818 * Marker class with button look-and-feel.
    19  * 
     19 *
    2020 * @author Frederik Ramm <frederik@remote.org>
    2121 *
     
    2323public class ButtonMarker extends Marker {
    2424
    25         private Rectangle buttonRectangle;
    26        
    27         public ButtonMarker(LatLon ll, String buttonImage, MarkerLayer parentLayer, double time, double offset) {
    28                 super(ll, null, buttonImage, parentLayer, time, offset);
    29                 buttonRectangle = new Rectangle(0, 0, symbol.getIconWidth(), symbol.getIconHeight());
    30         }
    31        
    32         public ButtonMarker(LatLon ll, String text, String buttonImage, MarkerLayer parentLayer, double time, double offset) {
    33                 super(ll, text, buttonImage, parentLayer, time, offset);
    34                 buttonRectangle = new Rectangle(0, 0, symbol.getIconWidth(), symbol.getIconHeight());
    35         }
    36        
    37         @Override public boolean containsPoint(Point p) {
    38                 Point screen = Main.map.mapView.getPoint(eastNorth);
    39                 buttonRectangle.setLocation(screen.x+4, screen.y+2);
    40                 return buttonRectangle.contains(p);
    41         }
    42        
    43         @Override public void paint(Graphics g, MapView mv, boolean mousePressed, String show) {
    44                 if (! show.equalsIgnoreCase("show")) {
    45                         super.paint(g, mv, mousePressed, show);
    46                         return;
    47                 }
    48                 Point screen = mv.getPoint(eastNorth);
    49                 buttonRectangle.setLocation(screen.x+4, screen.y+2);
    50                 symbol.paintIcon(mv, g, screen.x+4, screen.y+2);
    51                 Border b;
    52                 Point mousePosition = mv.getMousePosition();
    53                
    54                 if (mousePosition != null) {
    55                         // mouse is inside the window
    56                         if (mousePressed) {
    57                                 b = BorderFactory.createBevelBorder(BevelBorder.LOWERED);
    58                         } else {
    59                                 b = BorderFactory.createBevelBorder(BevelBorder.RAISED);
    60                         }
    61                         Insets inset = b.getBorderInsets(mv);
    62                         Rectangle r = new Rectangle(buttonRectangle);
    63                         r.grow((inset.top+inset.bottom)/2, (inset.left+inset.right)/2);
    64                         b.paintBorder(mv, g, r.x, r.y, r.width, r.height);
    65                 }
    66                 if ((text != null) && (show.equalsIgnoreCase("show")) && Main.pref.getBoolean("marker.buttonlabels", true))
    67                         g.drawString(text, screen.x+4, screen.y+2);
    68         }
     25    private Rectangle buttonRectangle;
     26
     27    public ButtonMarker(LatLon ll, String buttonImage, MarkerLayer parentLayer, double time, double offset) {
     28        super(ll, null, buttonImage, parentLayer, time, offset);
     29        buttonRectangle = new Rectangle(0, 0, symbol.getIconWidth(), symbol.getIconHeight());
     30    }
     31
     32    public ButtonMarker(LatLon ll, String text, String buttonImage, MarkerLayer parentLayer, double time, double offset) {
     33        super(ll, text, buttonImage, parentLayer, time, offset);
     34        buttonRectangle = new Rectangle(0, 0, symbol.getIconWidth(), symbol.getIconHeight());
     35    }
     36
     37    @Override public boolean containsPoint(Point p) {
     38        Point screen = Main.map.mapView.getPoint(eastNorth);
     39        buttonRectangle.setLocation(screen.x+4, screen.y+2);
     40        return buttonRectangle.contains(p);
     41    }
     42
     43    @Override public void paint(Graphics g, MapView mv, boolean mousePressed, String show) {
     44        if (! show.equalsIgnoreCase("show")) {
     45            super.paint(g, mv, mousePressed, show);
     46            return;
     47        }
     48        Point screen = mv.getPoint(eastNorth);
     49        buttonRectangle.setLocation(screen.x+4, screen.y+2);
     50        symbol.paintIcon(mv, g, screen.x+4, screen.y+2);
     51        Border b;
     52        Point mousePosition = mv.getMousePosition();
     53
     54        if (mousePosition != null) {
     55            // mouse is inside the window
     56            if (mousePressed) {
     57                b = BorderFactory.createBevelBorder(BevelBorder.LOWERED);
     58            } else {
     59                b = BorderFactory.createBevelBorder(BevelBorder.RAISED);
     60            }
     61            Insets inset = b.getBorderInsets(mv);
     62            Rectangle r = new Rectangle(buttonRectangle);
     63            r.grow((inset.top+inset.bottom)/2, (inset.left+inset.right)/2);
     64            b.paintBorder(mv, g, r.x, r.y, r.width, r.height);
     65        }
     66        if ((text != null) && (show.equalsIgnoreCase("show")) && Main.pref.getBoolean("marker.buttonlabels", true))
     67            g.drawString(text, screen.x+4, screen.y+2);
     68    }
    6969}
  • trunk/src/org/openstreetmap/josm/gui/layer/markerlayer/ImageMarker.java

    r627 r1169  
    2626 * Marker representing an image. Uses a special icon, and when clicked,
    2727 * displays an image view dialog. Re-uses some code from GeoImageLayer.
    28  * 
     28 *
    2929 * @author Frederik Ramm <frederik@remote.org>
    3030 *
     
    3232public class ImageMarker extends ButtonMarker {
    3333
    34         public URL imageUrl;
     34    public URL imageUrl;
    3535
    36         public static ImageMarker create(LatLon ll, String url, MarkerLayer parentLayer, double time, double offset) {
    37                 try {
    38                         return new ImageMarker(ll, new URL(url), parentLayer, time, offset);
    39                 } catch (Exception ex) {
    40                         return null;
    41                 }
    42         }
     36    public static ImageMarker create(LatLon ll, String url, MarkerLayer parentLayer, double time, double offset) {
     37        try {
     38            return new ImageMarker(ll, new URL(url), parentLayer, time, offset);
     39        } catch (Exception ex) {
     40            return null;
     41        }
     42    }
    4343
    44         private ImageMarker(LatLon ll, URL imageUrl, MarkerLayer parentLayer, double time, double offset) {
    45                 super(ll, "photo.png", parentLayer, time, offset);
    46                 this.imageUrl = imageUrl;
    47         }
     44    private ImageMarker(LatLon ll, URL imageUrl, MarkerLayer parentLayer, double time, double offset) {
     45        super(ll, "photo.png", parentLayer, time, offset);
     46        this.imageUrl = imageUrl;
     47    }
    4848
    49         @Override public void actionPerformed(ActionEvent ev) {
    50                 final JPanel p = new JPanel(new BorderLayout());
    51                 final JScrollPane scroll = new JScrollPane(new JLabel(loadScaledImage(imageUrl, 580)));
    52                 final JViewport vp = scroll.getViewport();
    53                 p.add(scroll, BorderLayout.CENTER);
     49    @Override public void actionPerformed(ActionEvent ev) {
     50        final JPanel p = new JPanel(new BorderLayout());
     51        final JScrollPane scroll = new JScrollPane(new JLabel(loadScaledImage(imageUrl, 580)));
     52        final JViewport vp = scroll.getViewport();
     53        p.add(scroll, BorderLayout.CENTER);
    5454
    55                 final JToggleButton scale = new JToggleButton(ImageProvider.get("misc", "rectangle"));
     55        final JToggleButton scale = new JToggleButton(ImageProvider.get("misc", "rectangle"));
    5656
    57                 JPanel p2 = new JPanel();
    58                 p2.add(scale);
    59                 p.add(p2, BorderLayout.SOUTH);
    60                 scale.addActionListener(new ActionListener(){
    61                         public void actionPerformed(ActionEvent ev) {
    62                                 p.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
    63                                 if (scale.getModel().isSelected())
    64                                         ((JLabel)vp.getView()).setIcon(loadScaledImage(imageUrl, Math.max(vp.getWidth(), vp.getHeight())));
    65                                 else
    66                                         ((JLabel)vp.getView()).setIcon(new ImageIcon(imageUrl));
    67                                 p.setCursor(Cursor.getDefaultCursor());
    68                         }
    69                 });
    70                 scale.setSelected(true);
    71                 JOptionPane pane = new JOptionPane(p, JOptionPane.PLAIN_MESSAGE);
    72                 JDialog dlg = pane.createDialog(Main.parent, imageUrl.toString());
    73                 dlg.setModal(false);
    74                 dlg.setVisible(true);
    75         }
     57        JPanel p2 = new JPanel();
     58        p2.add(scale);
     59        p.add(p2, BorderLayout.SOUTH);
     60        scale.addActionListener(new ActionListener(){
     61            public void actionPerformed(ActionEvent ev) {
     62                p.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
     63                if (scale.getModel().isSelected())
     64                    ((JLabel)vp.getView()).setIcon(loadScaledImage(imageUrl, Math.max(vp.getWidth(), vp.getHeight())));
     65                else
     66                    ((JLabel)vp.getView()).setIcon(new ImageIcon(imageUrl));
     67                p.setCursor(Cursor.getDefaultCursor());
     68            }
     69        });
     70        scale.setSelected(true);
     71        JOptionPane pane = new JOptionPane(p, JOptionPane.PLAIN_MESSAGE);
     72        JDialog dlg = pane.createDialog(Main.parent, imageUrl.toString());
     73        dlg.setModal(false);
     74        dlg.setVisible(true);
     75    }
    7676
    77         private static Icon loadScaledImage(URL u, int maxSize) {
    78                 Image img = new ImageIcon(u).getImage();
    79                 int w = img.getWidth(null);
    80                 int h = img.getHeight(null);
    81                 if (w>h) {
    82                         h = Math.round(maxSize*((float)h/w));
    83                         w = maxSize;
    84                 } else {
    85                         w = Math.round(maxSize*((float)w/h));
    86                         h = maxSize;
    87                 }
    88                 return new ImageIcon(img.getScaledInstance(w, h, Image.SCALE_SMOOTH));
    89         }
     77    private static Icon loadScaledImage(URL u, int maxSize) {
     78        Image img = new ImageIcon(u).getImage();
     79        int w = img.getWidth(null);
     80        int h = img.getHeight(null);
     81        if (w>h) {
     82            h = Math.round(maxSize*((float)h/w));
     83            w = maxSize;
     84        } else {
     85            w = Math.round(maxSize*((float)w/h));
     86            h = maxSize;
     87        }
     88        return new ImageIcon(img.getScaledInstance(w, h, Image.SCALE_SMOOTH));
     89    }
    9090
    9191}
  • trunk/src/org/openstreetmap/josm/gui/layer/markerlayer/Marker.java

    r655 r1169  
    2323
    2424/**
    25  * Basic marker class. Requires a position, and supports 
     25 * Basic marker class. Requires a position, and supports
    2626 * a custom icon and a name.
    27  * 
     27 *
    2828 * This class is also used to create appropriate Marker-type objects
    2929 * when waypoints are imported.
    30  * 
     30 *
    3131 * It hosts a public list object, named makers, containing implementations of
    32  * the MarkerMaker interface. Whenever a Marker needs to be created, each 
     32 * the MarkerMaker interface. Whenever a Marker needs to be created, each
    3333 * object in makers is called with the waypoint parameters (Lat/Lon and tag
    3434 * data), and the first one to return a Marker object wins.
    35  * 
     35 *
    3636 * By default, one the list contains one default "Maker" implementation that
    37  * will create AudioMarkers for .wav files, ImageMarkers for .png/.jpg/.jpeg 
     37 * will create AudioMarkers for .wav files, ImageMarkers for .png/.jpg/.jpeg
    3838 * files, and WebMarkers for everything else. (The creation of a WebMarker will
    3939 * fail if there's no vaild URL in the <link> tag, so it might still make sense
    4040 * to add Makers for such waypoints at the end of the list.)
    41  * 
     41 *
    4242 * The default implementation only looks at the value of the <link> tag inside
    4343 * the <wpt> tag of the GPX file.
    44  * 
     44 *
    4545 * <h2>HowTo implement a new Marker</h2>
    4646 * <ul>
     
    5454 *      if you only add a new marker style.</li>
    5555 * </ul>
    56  * 
     56 *
    5757 * @author Frederik Ramm <frederik@remote.org>
    5858 */
    5959public class Marker implements ActionListener {
    6060
    61         public EastNorth eastNorth;
    62         public final String text;
    63         public final Icon symbol;
    64         public final MarkerLayer parentLayer;
    65         public double time; /* avbsolute time of marker since epocj */
    66         public double offset; /* time offset in seconds from the gpx point from which it was derived,
    67                                                         may be adjusted later to sync with other data, so not final */
    68 
    69         /**
    70         * Plugins can add their Marker creation stuff at the bottom or top of this list
    71         * (depending on whether they want to override default behaviour or just add new
    72         * stuff).
    73         */
    74         public static LinkedList<MarkerProducers> markerProducers = new LinkedList<MarkerProducers>();
    75 
    76         // Add one Maker specifying the default behaviour.
    77         static {
    78                 Marker.markerProducers.add(new MarkerProducers() {
    79                         public Marker createMarker(WayPoint wpt, File relativePath, MarkerLayer parentLayer, double time, double offset) {
    80                                 String uri = null;
    81                                 // cheapest way to check whether "link" object exists and is a non-empty
    82                                 // collection of GpxLink objects...
    83                                 try {
    84                                         for (GpxLink oneLink : (Collection<GpxLink>) wpt.attr.get("link")) {
    85                                                 uri = oneLink.uri;
    86                                                 break;
    87                                         }
    88                                 } catch (Exception ex) {}
    89 
    90                                 // Try a relative file:// url, if the link is not in an URL-compatible form
     61    public EastNorth eastNorth;
     62    public final String text;
     63    public final Icon symbol;
     64    public final MarkerLayer parentLayer;
     65    public double time; /* avbsolute time of marker since epocj */
     66    public double offset; /* time offset in seconds from the gpx point from which it was derived,
     67                            may be adjusted later to sync with other data, so not final */
     68
     69    /**
     70    * Plugins can add their Marker creation stuff at the bottom or top of this list
     71    * (depending on whether they want to override default behaviour or just add new
     72    * stuff).
     73    */
     74    public static LinkedList<MarkerProducers> markerProducers = new LinkedList<MarkerProducers>();
     75
     76    // Add one Maker specifying the default behaviour.
     77    static {
     78        Marker.markerProducers.add(new MarkerProducers() {
     79            public Marker createMarker(WayPoint wpt, File relativePath, MarkerLayer parentLayer, double time, double offset) {
     80                String uri = null;
     81                // cheapest way to check whether "link" object exists and is a non-empty
     82                // collection of GpxLink objects...
     83                try {
     84                    for (GpxLink oneLink : (Collection<GpxLink>) wpt.attr.get("link")) {
     85                        uri = oneLink.uri;
     86                        break;
     87                    }
     88                } catch (Exception ex) {}
     89
     90                // Try a relative file:// url, if the link is not in an URL-compatible form
    9191                if (relativePath != null && uri != null && !isWellFormedAddress(uri))
    9292                    uri = new File(relativePath.getParentFile(), uri).toURI().toString();
     
    9494                String name_desc = "";
    9595                if (wpt.attr.containsKey("name")) {
    96                         name_desc = wpt.getString("name");
     96                    name_desc = wpt.getString("name");
    9797                } else if (wpt.attr.containsKey("desc")) {
    9898                    name_desc = wpt.getString("desc");
    9999                }
    100                
     100
    101101                if (uri == null)
    102102                    return new Marker(wpt.latlon, name_desc, wpt.getString("symbol"), parentLayer, time, offset);
     
    104104                    return AudioMarker.create(wpt.latlon, name_desc, uri, parentLayer, time, offset);
    105105                else if (uri.endsWith(".png") || uri.endsWith(".jpg") || uri.endsWith(".jpeg") || uri.endsWith(".gif"))
    106                                         return ImageMarker.create(wpt.latlon, uri, parentLayer, time, offset);
    107                                 else
    108                                         return WebMarker.create(wpt.latlon, uri, parentLayer, time, offset);
    109                         }
    110 
    111                         private boolean isWellFormedAddress(String link) {
    112                                 try {
    113                                         new URL(link);
    114                                         return true;
    115                                 } catch (MalformedURLException x) {
    116                                         return false;
    117                                 }
     106                    return ImageMarker.create(wpt.latlon, uri, parentLayer, time, offset);
     107                else
     108                    return WebMarker.create(wpt.latlon, uri, parentLayer, time, offset);
    118109            }
    119                 });
    120         }
    121 
    122         public Marker(LatLon ll, String text, String iconName, MarkerLayer parentLayer, double time, double offset) {
    123                 eastNorth = Main.proj.latlon2eastNorth(ll);
    124                 this.text = text;
    125                 this.offset = offset;
    126                 this.time = time;
    127                 Icon symbol = ImageProvider.getIfAvailable("markers",iconName);
    128                 if (symbol == null)
    129                         symbol = ImageProvider.getIfAvailable("symbols",iconName);
    130                 if (symbol == null)
    131                         symbol = ImageProvider.getIfAvailable("nodes",iconName);
    132                 this.symbol = symbol;
    133                 this.parentLayer = parentLayer;
    134         }
    135 
    136         /**
    137          * Checks whether the marker display area contains the given point.
    138          * Markers not interested in mouse clicks may always return false.
    139          *
    140          * @param p The point to check
    141          * @return <code>true</code> if the marker "hotspot" contains the point.
    142          */
    143         public boolean containsPoint(Point p) {
    144                 return false;
    145         }
    146 
    147         /**
    148          * Called when the mouse is clicked in the marker's hotspot. Never
    149          * called for markers which always return false from containsPoint.
    150          *
    151          * @param ev A dummy ActionEvent
    152          */
    153         public void actionPerformed(ActionEvent ev) {
    154         }
    155 
    156         /**
    157          * Paints the marker.
    158          * @param g graphics context
    159          * @param mv map view
    160          * @param mousePressed true if the left mouse button is pressed
    161          */
    162         public void paint(Graphics g, MapView mv, boolean mousePressed, String show) {
    163                 Point screen = mv.getPoint(eastNorth);
    164                 if (symbol != null && show.equalsIgnoreCase("show")) {
    165                         symbol.paintIcon(mv, g, screen.x-symbol.getIconWidth()/2, screen.y-symbol.getIconHeight()/2);
    166                 } else {
    167                         g.drawLine(screen.x-2, screen.y-2, screen.x+2, screen.y+2);
    168                         g.drawLine(screen.x+2, screen.y-2, screen.x-2, screen.y+2);
    169                 }
    170 
    171                 if ((text != null) && (show.equalsIgnoreCase("show")))
    172                         g.drawString(text, screen.x+4, screen.y+2);
    173         }
    174 
    175         /**
    176          * Returns an object of class Marker or one of its subclasses
    177          * created from the parameters given.
    178          *
    179          * @param wpt waypoint data for marker
    180          * @param relativePath An path to use for constructing relative URLs or
    181          *        <code>null</code> for no relative URLs
    182          * @param offset double in seconds as the time offset of this marker from
    183          *                the GPX file from which it was derived (if any). 
    184          * @return a new Marker object
    185          */
    186         public static Marker createMarker(WayPoint wpt, File relativePath, MarkerLayer parentLayer, double time, double offset) {
    187                 for (MarkerProducers maker : Marker.markerProducers) {
    188                         Marker marker = maker.createMarker(wpt, relativePath, parentLayer, time, offset);
    189                         if (marker != null)
    190                                 return marker;
    191                 }
    192                 return null;
    193         }
    194        
    195         /**
    196          * Returns an AudioMarker derived from this Marker and the provided uri
    197          * Subclasses of specific marker types override this to return null as they can't
    198          * be turned into AudioMarkers. This includes AudioMarkers themselves, as they
    199          * already have audio. 
    200          *
    201          * @param uri uri of wave file
    202          * @return AudioMarker
    203          */
    204        
    205         public AudioMarker audioMarkerFromMarker(String uri) {
    206                 AudioMarker audioMarker = AudioMarker.create(Main.proj.eastNorth2latlon(this.eastNorth), this.text, uri, this.parentLayer, this.time, this.offset);
    207                 return audioMarker;
    208         }
     110
     111            private boolean isWellFormedAddress(String link) {
     112                try {
     113                    new URL(link);
     114                    return true;
     115                } catch (MalformedURLException x) {
     116                    return false;
     117                }
     118            }
     119        });
     120    }
     121
     122    public Marker(LatLon ll, String text, String iconName, MarkerLayer parentLayer, double time, double offset) {
     123        eastNorth = Main.proj.latlon2eastNorth(ll);
     124        this.text = text;
     125        this.offset = offset;
     126        this.time = time;
     127        Icon symbol = ImageProvider.getIfAvailable("markers",iconName);
     128        if (symbol == null)
     129            symbol = ImageProvider.getIfAvailable("symbols",iconName);
     130        if (symbol == null)
     131            symbol = ImageProvider.getIfAvailable("nodes",iconName);
     132        this.symbol = symbol;
     133        this.parentLayer = parentLayer;
     134    }
     135
     136    /**
     137     * Checks whether the marker display area contains the given point.
     138     * Markers not interested in mouse clicks may always return false.
     139     *
     140     * @param p The point to check
     141     * @return <code>true</code> if the marker "hotspot" contains the point.
     142     */
     143    public boolean containsPoint(Point p) {
     144        return false;
     145    }
     146
     147    /**
     148     * Called when the mouse is clicked in the marker's hotspot. Never
     149     * called for markers which always return false from containsPoint.
     150     *
     151     * @param ev A dummy ActionEvent
     152     */
     153    public void actionPerformed(ActionEvent ev) {
     154    }
     155
     156    /**
     157     * Paints the marker.
     158     * @param g graphics context
     159     * @param mv map view
     160     * @param mousePressed true if the left mouse button is pressed
     161     */
     162    public void paint(Graphics g, MapView mv, boolean mousePressed, String show) {
     163        Point screen = mv.getPoint(eastNorth);
     164        if (symbol != null && show.equalsIgnoreCase("show")) {
     165            symbol.paintIcon(mv, g, screen.x-symbol.getIconWidth()/2, screen.y-symbol.getIconHeight()/2);
     166        } else {
     167            g.drawLine(screen.x-2, screen.y-2, screen.x+2, screen.y+2);
     168            g.drawLine(screen.x+2, screen.y-2, screen.x-2, screen.y+2);
     169        }
     170
     171        if ((text != null) && (show.equalsIgnoreCase("show")))
     172            g.drawString(text, screen.x+4, screen.y+2);
     173    }
     174
     175    /**
     176     * Returns an object of class Marker or one of its subclasses
     177     * created from the parameters given.
     178     *
     179     * @param wpt waypoint data for marker
     180     * @param relativePath An path to use for constructing relative URLs or
     181     *        <code>null</code> for no relative URLs
     182     * @param offset double in seconds as the time offset of this marker from
     183     *        the GPX file from which it was derived (if any).
     184     * @return a new Marker object
     185     */
     186    public static Marker createMarker(WayPoint wpt, File relativePath, MarkerLayer parentLayer, double time, double offset) {
     187        for (MarkerProducers maker : Marker.markerProducers) {
     188            Marker marker = maker.createMarker(wpt, relativePath, parentLayer, time, offset);
     189            if (marker != null)
     190                return marker;
     191        }
     192        return null;
     193    }
     194
     195    /**
     196     * Returns an AudioMarker derived from this Marker and the provided uri
     197     * Subclasses of specific marker types override this to return null as they can't
     198     * be turned into AudioMarkers. This includes AudioMarkers themselves, as they
     199     * already have audio.
     200     *
     201     * @param uri uri of wave file
     202     * @return AudioMarker
     203     */
     204
     205    public AudioMarker audioMarkerFromMarker(String uri) {
     206        AudioMarker audioMarker = AudioMarker.create(Main.proj.eastNorth2latlon(this.eastNorth), this.text, uri, this.parentLayer, this.time, this.offset);
     207        return audioMarker;
     208    }
    209209}
  • trunk/src/org/openstreetmap/josm/gui/layer/markerlayer/MarkerLayer.java

    r999 r1169  
    4343/**
    4444 * A layer holding markers.
    45  * 
     45 *
    4646 * Markers are GPS points with a name and, optionally, a symbol code attached;
    4747 * marker layers can be created from waypoints when importing raw GPS data,
    4848 * but they may also come from other sources.
    49  * 
     49 *
    5050 * The symbol code is for future use.
    51  * 
     51 *
    5252 * The data is read only.
    5353 */
    5454public class MarkerLayer extends Layer {
    5555
    56         /**
    57         * A list of markers.
    58         */
    59         public final Collection<Marker> data;
    60         private boolean mousePressed = false;
    61         public GpxLayer fromLayer = null;
    62        
    63         /*
    64         private Icon audioTracerIcon = null;
    65         private EastNorth playheadPosition = null;
    66         private static Timer timer = null;
    67         private static double audioAnimationInterval = 0.0; // seconds
    68         private static double playheadTime = -1.0;
    69         */
    70         public MarkerLayer(GpxData indata, String name, File associatedFile, GpxLayer fromLayer) {
    71                
    72                 super(name);
    73                 this.associatedFile = associatedFile;
    74                 this.data = new ArrayList<Marker>();
    75                 this.fromLayer = fromLayer;
    76                 double firstTime = -1.0;
    77                 String lastLinkedFile = "";
    78 
    79                 for (WayPoint wpt : indata.waypoints) {
    80                         /* calculate time differences in waypoints */
    81                         double time = wpt.time;
    82                         boolean wpt_has_link = wpt.attr.containsKey("link");
    83                         if (firstTime < 0 && wpt_has_link) {
    84                                 firstTime = time;
    85                                 for (GpxLink oneLink : (Collection<GpxLink>) wpt.attr.get("link")) {
    86                                         lastLinkedFile = oneLink.uri;
    87                                         break;
    88                                 }
    89                         }
    90                         if (wpt_has_link) {
    91                                 for (GpxLink oneLink : (Collection<GpxLink>) wpt.attr.get("link")) {
    92                                         if (!oneLink.uri.equals(lastLinkedFile))firstTime = time;
    93                                         lastLinkedFile = oneLink.uri;
    94                                         break;
    95                                 }
    96                         }
     56    /**
     57    * A list of markers.
     58    */
     59    public final Collection<Marker> data;
     60    private boolean mousePressed = false;
     61    public GpxLayer fromLayer = null;
     62
     63    /*
     64    private Icon audioTracerIcon = null;
     65    private EastNorth playheadPosition = null;
     66    private static Timer timer = null;
     67    private static double audioAnimationInterval = 0.0; // seconds
     68    private static double playheadTime = -1.0;
     69    */
     70    public MarkerLayer(GpxData indata, String name, File associatedFile, GpxLayer fromLayer) {
     71
     72        super(name);
     73        this.associatedFile = associatedFile;
     74        this.data = new ArrayList<Marker>();
     75        this.fromLayer = fromLayer;
     76        double firstTime = -1.0;
     77        String lastLinkedFile = "";
     78
     79        for (WayPoint wpt : indata.waypoints) {
     80            /* calculate time differences in waypoints */
     81            double time = wpt.time;
     82            boolean wpt_has_link = wpt.attr.containsKey("link");
     83            if (firstTime < 0 && wpt_has_link) {
     84                firstTime = time;
     85                for (GpxLink oneLink : (Collection<GpxLink>) wpt.attr.get("link")) {
     86                    lastLinkedFile = oneLink.uri;
     87                    break;
     88                }
     89            }
     90            if (wpt_has_link) {
     91                for (GpxLink oneLink : (Collection<GpxLink>) wpt.attr.get("link")) {
     92                    if (!oneLink.uri.equals(lastLinkedFile))firstTime = time;
     93                    lastLinkedFile = oneLink.uri;
     94                    break;
     95                }
     96            }
    9797            Marker m = Marker.createMarker(wpt, indata.storageFile, this, time, time - firstTime);
    9898            if (m != null)
    99                 data.add(m);
    100                 }
    101                
    102                 SwingUtilities.invokeLater(new Runnable(){
    103                         public void run() {
    104                                 Main.map.mapView.addMouseListener(new MouseAdapter() {
    105                                         @Override public void mousePressed(MouseEvent e) {
    106                                                 if (e.getButton() != MouseEvent.BUTTON1)
    107                                                         return;
    108                                                 boolean mousePressedInButton = false;
    109                                                 if (e.getPoint() != null) {
    110                                                         for (Marker mkr : data) {
    111                                                                 if (mkr.containsPoint(e.getPoint())) {
    112                                                                         mousePressedInButton = true;
    113                                                                         break;
    114                                                                 }
    115                                                         }
    116                                                 }
    117                                                 if (! mousePressedInButton)
    118                                                         return;
    119                                                 mousePressed  = true;
    120                                                 if (visible)
    121                                                         Main.map.mapView.repaint();
    122                                         }
    123                                         @Override public void mouseReleased(MouseEvent ev) {
    124                                                 if (ev.getButton() != MouseEvent.BUTTON1 || ! mousePressed)
    125                                                         return;
    126                                                 mousePressed = false;
    127                                                 if (!visible)
    128                                                         return;
    129                                                 if (ev.getPoint() != null) {
    130                                                         for (Marker mkr : data) {
    131                                                                 if (mkr.containsPoint(ev.getPoint()))
    132                                                                         mkr.actionPerformed(new ActionEvent(this, 0, null));
    133                                                         }
    134                                                 }
    135                                                 Main.map.mapView.repaint();
    136                                         }
    137                                 });
    138                         }
    139                 });
    140         }
    141 
    142         /**
    143         * Return a static icon.
    144         */
    145         @Override public Icon getIcon() {
    146                 return ImageProvider.get("layer", "marker_small");
    147         }
    148 
    149         @Override public void paint(Graphics g, MapView mv) {
    150                 boolean mousePressedTmp = mousePressed;
    151                 Point mousePos = mv.getMousePosition();
    152                 String mkrTextShow = Main.pref.get("marker.show "+name, "show");
    153 
    154                 g.setColor(Main.pref.getColor(marktr("gps marker"), "layer "+name, Color.gray));
    155                
    156                 for (Marker mkr : data) {
    157                         if (mousePos != null && mkr.containsPoint(mousePos)) {
    158                                 mkr.paint(g, mv, mousePressedTmp, mkrTextShow);
    159                                 mousePressedTmp = false;
    160                         } else {
    161                                 mkr.paint(g, mv, false, mkrTextShow);
    162                         }
    163                 }
    164         }
    165 
    166         @Override public String getToolTipText() {
    167                 return data.size()+" "+trn("marker", "markers", data.size());
    168         }
    169 
    170         @Override public void mergeFrom(Layer from) {
    171                 MarkerLayer layer = (MarkerLayer)from;
    172                 data.addAll(layer.data);
    173         }
    174 
    175         @Override public boolean isMergable(Layer other) {
    176                 return other instanceof MarkerLayer;
    177         }
    178 
    179         @Override public void visitBoundingBox(BoundingXYVisitor v) {
    180                 for (Marker mkr : data)
    181                         v.visit(mkr.eastNorth);
    182         }
    183 
    184         @Override public Object getInfoComponent() {
    185                 return "<html>"+trn("{0} consists of {1} marker", "{0} consists of {1} markers", data.size(), name, data.size()) + "</html>";
    186         }
    187 
    188         @Override public Component[] getMenuEntries() {
    189                 JMenuItem color = new JMenuItem(tr("Customize Color"), ImageProvider.get("colorchooser"));
    190                 color.putClientProperty("help", "Action/LayerCustomizeColor");
    191                 color.addActionListener(new ActionListener(){
    192                         public void actionPerformed(ActionEvent e) {
    193                                 JColorChooser c = new JColorChooser(Main.pref.getColor(marktr("gps marker"), "layer "+name, Color.gray));
    194                                 Object[] options = new Object[]{tr("OK"), tr("Cancel"), tr("Default")};
    195                                 int answer = JOptionPane.showOptionDialog(Main.parent, c, tr("Choose a color"), JOptionPane.OK_CANCEL_OPTION,
    196                                 JOptionPane.PLAIN_MESSAGE, null, options, options[0]);
    197                                 switch (answer) {
    198                                 case 0:
    199                                         Main.pref.putColor("layer "+name, c.getColor());
    200                                         break;
    201                                 case 1:
    202                                         return;
    203                                 case 2:
    204                                         Main.pref.putColor("layer "+name, null);
    205                                         break;
    206                                 }
    207                                 Main.map.repaint();
    208                         }
    209                 });
    210 
    211                 JMenuItem syncaudio = new JMenuItem(tr("Synchronize Audio"), ImageProvider.get("audio-sync"));
    212                 syncaudio.putClientProperty("help", "Action/SynchronizeAudio");
    213                 syncaudio.addActionListener(new ActionListener(){
    214                         public void actionPerformed(ActionEvent e) {
    215                                 if (! AudioPlayer.paused()) {
    216                                         JOptionPane.showMessageDialog(Main.parent,tr("You need to pause audio at the moment when you hear your synchronization cue."));
    217                                         return;
    218                                 }
    219                                 AudioMarker recent = AudioMarker.recentlyPlayedMarker();
    220                                 if (synchronizeAudioMarkers(recent)) {
    221                                         JOptionPane.showMessageDialog(Main.parent, tr("Audio synchronized at point {0}.", recent.text));
    222                                 } else {
    223                                         JOptionPane.showMessageDialog(Main.parent,tr("Unable to synchronize in layer being played."));
    224                                 }
    225                         }
    226                 });
    227 
    228                 JMenuItem moveaudio = new JMenuItem(tr("Make Audio Marker At Play Head"), ImageProvider.get("addmarkers"));
    229                 moveaudio.putClientProperty("help", "Action/MakeAudioMarkerAtPlayHead");
    230                 moveaudio.addActionListener(new ActionListener(){
    231                         public void actionPerformed(ActionEvent e) {
    232                                 if (! AudioPlayer.paused()) {
    233                                         JOptionPane.showMessageDialog(Main.parent,tr("You need to have paused audio at the point on the track where you want the marker."));
    234                                         return;
    235                                 }
    236                                 PlayHeadMarker playHeadMarker = Main.map.mapView.playHeadMarker;
    237                                 if (playHeadMarker == null)
    238                                         return;
    239                                 addAudioMarker(playHeadMarker.time, playHeadMarker.eastNorth);
    240                                 Main.map.mapView.repaint();
    241                         }
    242                 });
    243 
    244                 Collection<Component> components = new ArrayList<Component>();
    245                 components.add(new JMenuItem(new LayerListDialog.ShowHideLayerAction(this)));
    246                 components.add(new JMenuItem(new LayerListDialog.ShowHideMarkerText(this)));
    247                 components.add(new JMenuItem(new LayerListDialog.DeleteLayerAction(this)));
    248                 components.add(new JSeparator());
    249                 components.add(color);
    250                 components.add(new JSeparator());
    251                 components.add(syncaudio);
    252                 if (Main.pref.getBoolean("marker.traceaudio", true)) {
    253                         components.add (moveaudio);
    254                 }               
    255                 components.add(new JMenuItem(new RenameLayerAction(associatedFile, this)));
    256                 components.add(new JSeparator());
    257                 components.add(new JMenuItem(new LayerListPopup.InfoAction(this)));
    258                 return components.toArray(new Component[0]);
    259         }
    260 
    261         public boolean synchronizeAudioMarkers(AudioMarker startMarker) {
    262                 if (startMarker != null && ! data.contains(startMarker)) {
    263                         startMarker = null;
    264                 }
    265                 if (startMarker == null) {
    266                         // find the first audioMarker in this layer
    267                         for (Marker m : data) {
    268                                 if (m instanceof AudioMarker) {
    269                                         startMarker = (AudioMarker) m;
    270                                         break;
    271                                 }
    272                         }
    273                 }
    274                 if (startMarker == null)
    275                         return false;
    276                        
    277                 // apply adjustment to all subsequent audio markers in the layer
    278                 double adjustment = AudioPlayer.position() - startMarker.offset; // in seconds
    279                 boolean seenStart = false;
    280                 URL url = startMarker.url();
    281                 for (Marker m : data) {
    282                         if (m == startMarker)
    283                                 seenStart = true;
    284                         if (seenStart) {
    285                                 AudioMarker ma = (AudioMarker) m; // it must be an AudioMarker
    286                                 if (ma.url().equals(url))
    287                                         ma.adjustOffset(adjustment);
    288                         }
    289                 }
    290                 return true;
    291         }
    292        
    293         public AudioMarker addAudioMarker(double time, EastNorth en) {
    294                 // find first audio marker to get absolute start time
    295                 double offset = 0.0;
    296                 AudioMarker am = null;
    297                 for (Marker m : data) {
    298                         if (m.getClass() == AudioMarker.class) {
    299                                 am = (AudioMarker)m;
    300                                 offset = time - am.time;
    301                                 break;
    302                         }
    303                 }
    304                 if (am == null) {
    305                         JOptionPane.showMessageDialog(Main.parent,tr("No existing audio markers in this layer to offset from."));
    306                         return null;
    307                 }
    308 
    309                 // make our new marker
    310                 AudioMarker newAudioMarker = AudioMarker.create(Main.proj.eastNorth2latlon(en),
    311                         AudioMarker.inventName(offset), AudioPlayer.url().toString(), this, time, offset);
    312 
    313                 // insert it at the right place in a copy the collection
    314                 Collection<Marker> newData = new ArrayList<Marker>();
    315                 am = null;
    316                 AudioMarker ret = newAudioMarker; // save to have return value
    317                 for (Marker m : data) {
    318                         if (m.getClass() == AudioMarker.class) {
    319                                 am = (AudioMarker) m;
    320                                 if (newAudioMarker != null && offset < am.offset) {
    321                                         newAudioMarker.adjustOffset(am.syncOffset()); // i.e. same as predecessor
    322                                         newData.add(newAudioMarker);
    323                                         newAudioMarker = null;
    324                                 }
    325                         }
    326                         newData.add(m);
    327                 }
    328 
    329                 if (newAudioMarker != null) {
    330                         if (am != null)
    331                                 newAudioMarker.adjustOffset(am.syncOffset()); // i.e. same as predecessor
    332                         newData.add(newAudioMarker); // insert at end
    333                 }
    334 
    335                 // replace the collection
    336                 data.clear();
    337                 data.addAll(newData);
    338                 return ret;
    339         }
    340        
    341         public static void playAudio() {
    342                 if (Main.map == null || Main.map.mapView == null)
    343                         return;
    344                 for (Layer layer : Main.map.mapView.getAllLayers()) {
    345                         if (layer.getClass() == MarkerLayer.class) {
    346                                 MarkerLayer markerLayer = (MarkerLayer) layer;
    347                                 for (Marker marker : markerLayer.data) {
    348                                         if (marker.getClass() == AudioMarker.class) {
    349                                                 ((AudioMarker)marker).play();
    350                                                 break;
    351                                         }
    352                                 }
    353                         }
    354                 }
    355         }
    356 
    357         public static void playNextMarker() {
    358                 playAdjacentMarker(true);
    359         }
    360        
    361         public static void playPreviousMarker() {
    362                 playAdjacentMarker(false);
    363         }
    364        
    365         private static void playAdjacentMarker(boolean next) {
    366                 Marker startMarker = AudioMarker.recentlyPlayedMarker();
    367                 if (startMarker == null) {
    368                         // message?
    369                         return;
    370                 }
    371                 Marker previousMarker = null;
    372                 boolean nextTime = false;
    373                 if (Main.map == null || Main.map.mapView == null)
    374                         return;
    375                 for (Layer layer : Main.map.mapView.getAllLayers()) {
    376                         if (layer.getClass() == MarkerLayer.class) {
    377                                 MarkerLayer markerLayer = (MarkerLayer) layer;
    378                                 for (Marker marker : markerLayer.data) {
    379                                         if (marker == startMarker) {
    380                                                 if (next) {
    381                                                         nextTime = true;
    382                                                 } else {
    383                                                         if (previousMarker == null)
    384                                                                 previousMarker = startMarker; // if no previous one, play the first one again
    385                                                         ((AudioMarker)previousMarker).play();
    386                                                         break;
    387                                                 }
    388                                         } else if (nextTime && marker.getClass() == AudioMarker.class) {
    389                                                 ((AudioMarker)marker).play();
    390                                                 return;
    391                                         }
    392                                         if (marker.getClass() == AudioMarker.class)
    393                                                 previousMarker = marker;
    394                                 }
    395                                 if (nextTime) {
    396                                         // there was no next marker in that layer, so play the last one again
    397                                         ((AudioMarker)startMarker).play();
    398                                         return;
    399                                 }
    400                         }
    401                 }
    402         }
    403        
     99                data.add(m);
     100        }
     101
     102        SwingUtilities.invokeLater(new Runnable(){
     103            public void run() {
     104                Main.map.mapView.addMouseListener(new MouseAdapter() {
     105                    @Override public void mousePressed(MouseEvent e) {
     106                        if (e.getButton() != MouseEvent.BUTTON1)
     107                            return;
     108                        boolean mousePressedInButton = false;
     109                        if (e.getPoint() != null) {
     110                            for (Marker mkr : data) {
     111                                if (mkr.containsPoint(e.getPoint())) {
     112                                    mousePressedInButton = true;
     113                                    break;
     114                                }
     115                            }
     116                        }
     117                        if (! mousePressedInButton)
     118                            return;
     119                        mousePressed  = true;
     120                        if (visible)
     121                            Main.map.mapView.repaint();
     122                    }
     123                    @Override public void mouseReleased(MouseEvent ev) {
     124                        if (ev.getButton() != MouseEvent.BUTTON1 || ! mousePressed)
     125                            return;
     126                        mousePressed = false;
     127                        if (!visible)
     128                            return;
     129                        if (ev.getPoint() != null) {
     130                            for (Marker mkr : data) {
     131                                if (mkr.containsPoint(ev.getPoint()))
     132                                    mkr.actionPerformed(new ActionEvent(this, 0, null));
     133                            }
     134                        }
     135                        Main.map.mapView.repaint();
     136                    }
     137                });
     138            }
     139        });
     140    }
     141
     142    /**
     143    * Return a static icon.
     144    */
     145    @Override public Icon getIcon() {
     146        return ImageProvider.get("layer", "marker_small");
     147    }
     148
     149    @Override public void paint(Graphics g, MapView mv) {
     150        boolean mousePressedTmp = mousePressed;
     151        Point mousePos = mv.getMousePosition();
     152        String mkrTextShow = Main.pref.get("marker.show "+name, "show");
     153
     154        g.setColor(Main.pref.getColor(marktr("gps marker"), "layer "+name, Color.gray));
     155
     156        for (Marker mkr : data) {
     157            if (mousePos != null && mkr.containsPoint(mousePos)) {
     158                mkr.paint(g, mv, mousePressedTmp, mkrTextShow);
     159                mousePressedTmp = false;
     160            } else {
     161                mkr.paint(g, mv, false, mkrTextShow);
     162            }
     163        }
     164    }
     165
     166    @Override public String getToolTipText() {
     167        return data.size()+" "+trn("marker", "markers", data.size());
     168    }
     169
     170    @Override public void mergeFrom(Layer from) {
     171        MarkerLayer layer = (MarkerLayer)from;
     172        data.addAll(layer.data);
     173    }
     174
     175    @Override public boolean isMergable(Layer other) {
     176        return other instanceof MarkerLayer;
     177    }
     178
     179    @Override public void visitBoundingBox(BoundingXYVisitor v) {
     180        for (Marker mkr : data)
     181            v.visit(mkr.eastNorth);
     182    }
     183
     184    @Override public Object getInfoComponent() {
     185        return "<html>"+trn("{0} consists of {1} marker", "{0} consists of {1} markers", data.size(), name, data.size()) + "</html>";
     186    }
     187
     188    @Override public Component[] getMenuEntries() {
     189        JMenuItem color = new JMenuItem(tr("Customize Color"), ImageProvider.get("colorchooser"));
     190        color.putClientProperty("help", "Action/LayerCustomizeColor");
     191        color.addActionListener(new ActionListener(){
     192            public void actionPerformed(ActionEvent e) {
     193                JColorChooser c = new JColorChooser(Main.pref.getColor(marktr("gps marker"), "layer "+name, Color.gray));
     194                Object[] options = new Object[]{tr("OK"), tr("Cancel"), tr("Default")};
     195                int answer = JOptionPane.showOptionDialog(Main.parent, c, tr("Choose a color"), JOptionPane.OK_CANCEL_OPTION,
     196                JOptionPane.PLAIN_MESSAGE, null, options, options[0]);
     197                switch (answer) {
     198                case 0:
     199                    Main.pref.putColor("layer "+name, c.getColor());
     200                    break;
     201                case 1:
     202                    return;
     203                case 2:
     204                    Main.pref.putColor("layer "+name, null);
     205                    break;
     206                }
     207                Main.map.repaint();
     208            }
     209        });
     210
     211        JMenuItem syncaudio = new JMenuItem(tr("Synchronize Audio"), ImageProvider.get("audio-sync"));
     212        syncaudio.putClientProperty("help", "Action/SynchronizeAudio");
     213        syncaudio.addActionListener(new ActionListener(){
     214            public void actionPerformed(ActionEvent e) {
     215                if (! AudioPlayer.paused()) {
     216                    JOptionPane.showMessageDialog(Main.parent,tr("You need to pause audio at the moment when you hear your synchronization cue."));
     217                    return;
     218                }
     219                AudioMarker recent = AudioMarker.recentlyPlayedMarker();
     220                if (synchronizeAudioMarkers(recent)) {
     221                    JOptionPane.showMessageDialog(Main.parent, tr("Audio synchronized at point {0}.", recent.text));
     222                } else {
     223                    JOptionPane.showMessageDialog(Main.parent,tr("Unable to synchronize in layer being played."));
     224                }
     225            }
     226        });
     227
     228        JMenuItem moveaudio = new JMenuItem(tr("Make Audio Marker At Play Head"), ImageProvider.get("addmarkers"));
     229        moveaudio.putClientProperty("help", "Action/MakeAudioMarkerAtPlayHead");
     230        moveaudio.addActionListener(new ActionListener(){
     231            public void actionPerformed(ActionEvent e) {
     232                if (! AudioPlayer.paused()) {
     233                    JOptionPane.showMessageDialog(Main.parent,tr("You need to have paused audio at the point on the track where you want the marker."));
     234                    return;
     235                }
     236                PlayHeadMarker playHeadMarker = Main.map.mapView.playHeadMarker;
     237                if (playHeadMarker == null)
     238                    return;
     239                addAudioMarker(playHeadMarker.time, playHeadMarker.eastNorth);
     240                Main.map.mapView.repaint();
     241            }
     242        });
     243
     244        Collection<Component> components = new ArrayList<Component>();
     245        components.add(new JMenuItem(new LayerListDialog.ShowHideLayerAction(this)));
     246        components.add(new JMenuItem(new LayerListDialog.ShowHideMarkerText(this)));
     247        components.add(new JMenuItem(new LayerListDialog.DeleteLayerAction(this)));
     248        components.add(new JSeparator());
     249        components.add(color);
     250        components.add(new JSeparator());
     251        components.add(syncaudio);
     252        if (Main.pref.getBoolean("marker.traceaudio", true)) {
     253            components.add (moveaudio);
     254        }
     255        components.add(new JMenuItem(new RenameLayerAction(associatedFile, this)));
     256        components.add(new JSeparator());
     257        components.add(new JMenuItem(new LayerListPopup.InfoAction(this)));
     258        return components.toArray(new Component[0]);
     259    }
     260
     261    public boolean synchronizeAudioMarkers(AudioMarker startMarker) {
     262        if (startMarker != null && ! data.contains(startMarker)) {
     263            startMarker = null;
     264        }
     265        if (startMarker == null) {
     266            // find the first audioMarker in this layer
     267            for (Marker m : data) {
     268                if (m instanceof AudioMarker) {
     269                    startMarker = (AudioMarker) m;
     270                    break;
     271                }
     272            }
     273        }
     274        if (startMarker == null)
     275            return false;
     276
     277        // apply adjustment to all subsequent audio markers in the layer
     278        double adjustment = AudioPlayer.position() - startMarker.offset; // in seconds
     279        boolean seenStart = false;
     280        URL url = startMarker.url();
     281        for (Marker m : data) {
     282            if (m == startMarker)
     283                seenStart = true;
     284            if (seenStart) {
     285                AudioMarker ma = (AudioMarker) m; // it must be an AudioMarker
     286                if (ma.url().equals(url))
     287                    ma.adjustOffset(adjustment);
     288            }
     289        }
     290        return true;
     291    }
     292
     293    public AudioMarker addAudioMarker(double time, EastNorth en) {
     294        // find first audio marker to get absolute start time
     295        double offset = 0.0;
     296        AudioMarker am = null;
     297        for (Marker m : data) {
     298            if (m.getClass() == AudioMarker.class) {
     299                am = (AudioMarker)m;
     300                offset = time - am.time;
     301                break;
     302            }
     303        }
     304        if (am == null) {
     305            JOptionPane.showMessageDialog(Main.parent,tr("No existing audio markers in this layer to offset from."));
     306            return null;
     307        }
     308
     309        // make our new marker
     310        AudioMarker newAudioMarker = AudioMarker.create(Main.proj.eastNorth2latlon(en),
     311            AudioMarker.inventName(offset), AudioPlayer.url().toString(), this, time, offset);
     312
     313        // insert it at the right place in a copy the collection
     314        Collection<Marker> newData = new ArrayList<Marker>();
     315        am = null;
     316        AudioMarker ret = newAudioMarker; // save to have return value
     317        for (Marker m : data) {
     318            if (m.getClass() == AudioMarker.class) {
     319                am = (AudioMarker) m;
     320                if (newAudioMarker != null && offset < am.offset) {
     321                    newAudioMarker.adjustOffset(am.syncOffset()); // i.e. same as predecessor
     322                    newData.add(newAudioMarker);
     323                    newAudioMarker = null;
     324                }
     325            }
     326            newData.add(m);
     327        }
     328
     329        if (newAudioMarker != null) {
     330            if (am != null)
     331                newAudioMarker.adjustOffset(am.syncOffset()); // i.e. same as predecessor
     332            newData.add(newAudioMarker); // insert at end
     333        }
     334
     335        // replace the collection
     336        data.clear();
     337        data.addAll(newData);
     338        return ret;
     339    }
     340
     341    public static void playAudio() {
     342        if (Main.map == null || Main.map.mapView == null)
     343            return;
     344        for (Layer layer : Main.map.mapView.getAllLayers()) {
     345            if (layer.getClass() == MarkerLayer.class) {
     346                MarkerLayer markerLayer = (MarkerLayer) layer;
     347                for (Marker marker : markerLayer.data) {
     348                    if (marker.getClass() == AudioMarker.class) {
     349                        ((AudioMarker)marker).play();
     350                        break;
     351                    }
     352                }
     353            }
     354        }
     355    }
     356
     357    public static void playNextMarker() {
     358        playAdjacentMarker(true);
     359    }
     360
     361    public static void playPreviousMarker() {
     362        playAdjacentMarker(false);
     363    }
     364
     365    private static void playAdjacentMarker(boolean next) {
     366        Marker startMarker = AudioMarker.recentlyPlayedMarker();
     367        if (startMarker == null) {
     368            // message?
     369            return;
     370        }
     371        Marker previousMarker = null;
     372        boolean nextTime = false;
     373        if (Main.map == null || Main.map.mapView == null)
     374            return;
     375        for (Layer layer : Main.map.mapView.getAllLayers()) {
     376            if (layer.getClass() == MarkerLayer.class) {
     377                MarkerLayer markerLayer = (MarkerLayer) layer;
     378                for (Marker marker : markerLayer.data) {
     379                    if (marker == startMarker) {
     380                        if (next) {
     381                            nextTime = true;
     382                        } else {
     383                            if (previousMarker == null)
     384                                previousMarker = startMarker; // if no previous one, play the first one again
     385                            ((AudioMarker)previousMarker).play();
     386                            break;
     387                        }
     388                    } else if (nextTime && marker.getClass() == AudioMarker.class) {
     389                        ((AudioMarker)marker).play();
     390                        return;
     391                    }
     392                    if (marker.getClass() == AudioMarker.class)
     393                        previousMarker = marker;
     394                }
     395                if (nextTime) {
     396                    // there was no next marker in that layer, so play the last one again
     397                    ((AudioMarker)startMarker).play();
     398                    return;
     399                }
     400            }
     401        }
     402    }
     403
    404404}
  • trunk/src/org/openstreetmap/josm/gui/layer/markerlayer/MarkerProducers.java

    r655 r1169  
    88/**
    99 * This interface has to be implemented by anyone who wants to create markers.
    10  * 
    11  * When reading a gpx file, all implementations of MarkerMaker registered with 
     10 *
     11 * When reading a gpx file, all implementations of MarkerMaker registered with
    1212 * the Marker are consecutively called until one returns a Marker object.
    13  * 
     13 *
    1414 * @author Frederik Ramm <frederik@remote.org>
    1515 */
    1616public interface MarkerProducers {
    17         /**
    18         * Returns a Marker object if this implementation wants to create one for the
    19         * given input data, or <code>null</code> otherwise.
    20          *
    21          * @param wp waypoint data
    22          * @param relativePath An path to use for constructing relative URLs or
    23         *        <code>null</code> for no relative URLs
    24         * @return A Marker object, or <code>null</code>.
    25         */
    26         public Marker createMarker(WayPoint wp, File relativePath, MarkerLayer parentLayer, double time, double offset);
     17    /**
     18    * Returns a Marker object if this implementation wants to create one for the
     19    * given input data, or <code>null</code> otherwise.
     20     *
     21     * @param wp waypoint data
     22     * @param relativePath An path to use for constructing relative URLs or
     23    *        <code>null</code> for no relative URLs
     24    * @return A Marker object, or <code>null</code>.
     25    */
     26    public Marker createMarker(WayPoint wp, File relativePath, MarkerLayer parentLayer, double time, double offset);
    2727}
  • trunk/src/org/openstreetmap/josm/gui/layer/markerlayer/PlayHeadMarker.java

    r1026 r1169  
    3131/**
    3232 * Singleton marker class to track position of audio.
    33  * 
     33 *
    3434 * @author David Earl<david@frankieandshadow.com>
    3535 *
     
    3737public class PlayHeadMarker extends Marker {
    3838
    39         private Timer timer = null;
    40         private double animationInterval = 0.0; // seconds
    41         // private Rectangle audioTracer = null;
    42         // private Icon audioTracerIcon = null;
    43         static private PlayHeadMarker playHead = null;
    44         private MapMode oldMode = null;
    45         private EastNorth oldEastNorth;
    46         private boolean enabled;
    47         private boolean wasPlaying = false;
    48         private int dropTolerance = 50; /* pixels */
    49        
    50         public static PlayHeadMarker create() {
    51                 if (playHead == null) {
    52                         try {
    53                                 playHead = new PlayHeadMarker();
    54                         } catch (Exception ex) {
    55                                 return null;
    56                         }
    57                 }
    58                 return playHead;
    59         }
    60        
    61         private PlayHeadMarker() {
    62                 super(new LatLon(0.0,0.0), "",
    63                           Main.pref.get("marker.audiotracericon", "audio-tracer"),
    64                           null, -1.0, 0.0);
    65                 enabled = Main.pref.getBoolean("marker.traceaudio", true);
    66                 if (! enabled) return;
    67                 try { dropTolerance = Integer.parseInt(Main.pref.get("marker.playHeadDropTolerance", "50")); }
    68                 catch(NumberFormatException x) { dropTolerance = 50; }
    69                 Main.map.mapView.addMouseListener(new MouseAdapter() {
    70                         @Override public void mousePressed(MouseEvent ev) {
    71                                 Point p = ev.getPoint();
    72                                 if (ev.getButton() != MouseEvent.BUTTON1 || p == null)
    73                                         return;
    74                                 if (playHead.containsPoint(p)) {
    75                                         /* when we get a click on the marker, we need to switch mode to avoid
    76                                         * getting confused with other drag operations (like select) */
    77                                         oldMode = Main.map.mapMode;
    78                                         oldEastNorth = eastNorth;
    79                                         PlayHeadDragMode playHeadDragMode = new PlayHeadDragMode(playHead);
    80                                         Main.map.selectMapMode(playHeadDragMode);
    81                                         playHeadDragMode.mousePressed(ev);
    82                                 }
    83                         }
    84                 });
    85         }
    86 
    87         @Override public boolean containsPoint(Point p) {
    88                 Point screen = Main.map.mapView.getPoint(eastNorth);
    89                 Rectangle r = new Rectangle(screen.x, screen.y, symbol.getIconWidth(), symbol.getIconHeight());
    90                 return r.contains(p);
    91         }
    92 
    93         /**
    94          * called back from drag mode to say when we started dragging for real
    95         * (at least a short distance)
    96         */
    97         public void startDrag() {
    98                 if (timer != null)
    99                         timer.stop();
    100                 wasPlaying = AudioPlayer.playing();
    101                 if (wasPlaying) {
    102                         try { AudioPlayer.pause(); }
    103                     catch (Exception ex) { AudioPlayer.audioMalfunction(ex);}
    104                 }
    105         }
    106 
    107         /**
    108          * reinstate the old map mode after swuitching temporarily to do a play head drag
    109         */
    110         private void endDrag(boolean reset) {
    111                 if (! wasPlaying || reset)
    112                         try { AudioPlayer.pause(); }
    113                         catch (Exception ex) { AudioPlayer.audioMalfunction(ex);}
    114                 if (reset)
    115                         eastNorth = oldEastNorth;
    116                 Main.map.selectMapMode(oldMode);
    117                 Main.map.mapView.repaint();
    118                 timer.start();
    119         }
    120        
    121         /**
    122         * apply the new position resulting from a drag in progress
    123          * @param en the new position in map terms
    124         */
    125         public void drag(EastNorth en) {
    126                 eastNorth = en;
    127                 Main.map.mapView.repaint();
    128         }
    129 
    130         /**
    131          * Find the closest track point within the pixelTolerance of the screen point pNear
    132          * @param pNear : the point in screen coordinates near which to find a track point
    133         * @param pixelTolerance : only accept the point if within this number of pixels of en
    134         * @return the nearest trackpoint or null if nothing nearby
    135          *
    136         * XXX seems unused, F.R. 2008-03-15
    137         private WayPoint getClosestTrackPoint(Point pNear, double pixelTolerance) {
    138                 WayPoint cw = null;
    139                 AudioMarker recentlyPlayedMarker = AudioMarker.recentlyPlayedMarker();
    140                 if (recentlyPlayedMarker != null) {
    141                         // Find the track point closest to letting go of the play head
    142                         double minDistance = pixelTolerance;
    143                         GpxLayer trackLayer = recentlyPlayedMarker.parentLayer.fromLayer;
    144                         if (trackLayer.data.tracks == null)
    145                                 return null;
    146                        
    147                         for (GpxTrack track : trackLayer.data.tracks) {
    148                                 if (track.trackSegs == null)
    149                                         continue;
    150 
    151                                 for (Collection<WayPoint> trackseg : track.trackSegs) {
    152                                         for (WayPoint w : trackseg) {
    153                                                 Point p = Main.map.mapView.getPoint(w.eastNorth);
    154                                                 double distance = p.distance(pNear);
    155                                                 if (distance <= minDistance) {
    156                                                         cw = w;
    157                                                         minDistance = distance;
    158                                                 }
    159                                         }
    160                                 }
    161                         }
    162                 }
    163                 return cw;
    164         }
    165         */
    166        
    167         /**
    168         * reposition the play head at the point on the track nearest position given,
    169         * providing we are within reasonable distance from the track; otherwise reset to the
    170         * original position.
    171         * @param en the position to start looking from
    172         */
    173         public void reposition(EastNorth en) {
    174                 // eastNorth = en;
    175                 WayPoint cw = null;
    176                 AudioMarker recent = AudioMarker.recentlyPlayedMarker();
    177                 if (recent != null && recent.parentLayer != null && recent.parentLayer.fromLayer != null) {
    178                         /* work out EastNorth equivalent of 50 (default) pixels tolerance */
    179                         Point p = Main.map.mapView.getPoint(en);
    180                         EastNorth enPlus25px = Main.map.mapView.getEastNorth(p.x+dropTolerance, p.y);
    181                         cw = recent.parentLayer.fromLayer.nearestPointOnTrack(en, enPlus25px.east() - en.east());
    182                 }
    183                
    184                 AudioMarker ca = null;
    185                 /* Find the prior audio marker (there should always be one in the
    186                  * layer, even if it is only one at the start of the track) to
    187                  * offset the audio from */
    188                 if (cw != null) {
    189                         if (recent != null && recent.parentLayer != null) {
    190                                 for (Marker m : recent.parentLayer.data) {
    191                                         if (m instanceof AudioMarker) {
    192                                                 AudioMarker a = (AudioMarker) m;
    193                                                 if (a.time > cw.time)
    194                                                         break;
    195                                                 ca = a;
    196                                         }
    197                                 }
    198                         }
    199                 }
    200 
    201                 if (ca == null) {
    202                         /* Not close enough to track, or no audio marker found for some other reason */
    203                         JOptionPane.showMessageDialog(Main.parent, tr("You need to Drag the play head near to the GPX track whose associated sound track you were playing."));
    204                         endDrag(true);
    205                 } else {
    206                         eastNorth = cw.eastNorth;
    207                         ca.play(cw.time - ca.time);
    208                         endDrag(false);
    209                 }
    210         }
    211 
    212         /**
    213          * Synchronize the audio at the position where the play head was paused before
    214          * dragging with the position on the track where it was dropped.
    215          * If this is quite near an audio marker, we use that
    216          * marker as the sync. location, otherwise we create a new marker at the
    217          * trackpoint nearest the end point of the drag point to apply the
    218         * sync to.
    219         * @param en : the EastNorth end point of the drag
    220         */
    221         public void synchronize(EastNorth en) {
    222                 AudioMarker recent = AudioMarker.recentlyPlayedMarker();
    223                 if(recent == null)
    224                         return;
    225                 /* First, see if we dropped onto an existing audio marker in the layer being played */
    226                 Point startPoint = Main.map.mapView.getPoint(en);
    227                 AudioMarker ca = null;
    228                 if (recent.parentLayer != null) {
    229                         double closestAudioMarkerDistanceSquared = 1.0E100;
    230                         for (Marker m : recent.parentLayer.data) {
    231                                 if (m instanceof AudioMarker) {
    232                                         double distanceSquared = m.eastNorth.distanceSq(en);
    233                                         if (distanceSquared < closestAudioMarkerDistanceSquared) {
    234                                                 ca = (AudioMarker) m;
    235                                                 closestAudioMarkerDistanceSquared = distanceSquared;
    236                                         }
    237                                 }
    238                         }
    239                 }
    240 
    241                 /* We found the closest marker: did we actually hit it? */
    242                 if (ca != null && ! ca.containsPoint(startPoint)) ca = null;
    243 
    244                 /* If we didn't hit an audio marker, we need to create one at the nearest point on the track */
    245                 if (ca == null) {
    246                         /* work out EastNorth equivalent of 50 (default) pixels tolerance */
    247                         Point p = Main.map.mapView.getPoint(en);
    248                         EastNorth enPlus25px = Main.map.mapView.getEastNorth(p.x+dropTolerance, p.y);
    249                         WayPoint cw = recent.parentLayer.fromLayer.nearestPointOnTrack(en, enPlus25px.east() - en.east());
    250                         if (cw == null) {
    251                                 JOptionPane.showMessageDialog(Main.parent, tr("You need to SHIFT-Drag the play head onto an audio marker or onto the track point where you want to synchronize."));
    252                                 endDrag(true);
    253                                 return;
    254                         }
    255                         ca = recent.parentLayer.addAudioMarker(cw.time, cw.eastNorth);
    256                 }
    257 
    258                 /* Actually do the synchronization */
    259                 if(ca == null)
    260                 {
    261                         JOptionPane.showMessageDialog(Main.parent,tr("Unable to create new Audio marker."));
    262                         endDrag(true);
    263                 }
    264                 else if (recent.parentLayer.synchronizeAudioMarkers(ca)) {
    265                         JOptionPane.showMessageDialog(Main.parent, tr("Audio synchronized at point {0}.", ca.text));
    266                         eastNorth = ca.eastNorth;
    267                         endDrag(false);
    268                 } else {
    269                         JOptionPane.showMessageDialog(Main.parent,tr("Unable to synchronize in layer being played."));
    270                         endDrag(true);
    271                 }
    272         }
    273        
    274         public void paint(Graphics g, MapView mv /*, boolean mousePressed */) {
    275                 if (time < 0.0) return;
    276                 Point screen = mv.getPoint(eastNorth);
    277                 symbol.paintIcon(mv, g, screen.x, screen.y);
    278         }
    279 
    280         public void animate() {
    281                 if (! enabled) return;
    282                 if (timer == null) {
    283                         animationInterval = Double.parseDouble(Main.pref.get("marker.audioanimationinterval", "1")); //milliseconds
    284                         timer = new Timer((int)(animationInterval * 1000.0), new ActionListener() {
    285                                 public void actionPerformed(ActionEvent e) {
    286                                         timerAction();
    287                                 }
    288                         });
    289                         timer.setInitialDelay(0);
    290                 } else {
    291                         timer.stop();
    292                 }
    293                 timer.start();
    294         }
    295        
    296         /**
    297          * callback for moving play head marker according to audio player position
    298         */
    299         public void timerAction() {
    300                 AudioMarker recentlyPlayedMarker = AudioMarker.recentlyPlayedMarker();
    301                 if (recentlyPlayedMarker == null)
    302                         return;
    303                 double audioTime = recentlyPlayedMarker.time +
    304                         AudioPlayer.position() -
    305                         recentlyPlayedMarker.offset -
    306                         recentlyPlayedMarker.syncOffset;
    307                 if (Math.abs(audioTime - time) < animationInterval)
    308                         return;
    309                 if (recentlyPlayedMarker.parentLayer == null) return;
    310                 GpxLayer trackLayer = recentlyPlayedMarker.parentLayer.fromLayer;
    311                 if (trackLayer == null)
    312                         return;
    313                 /* find the pair of track points for this position (adjusted by the syncOffset)
    314                  * and interpolate between them
    315                 */
    316                 WayPoint w1 = null;
    317                 WayPoint w2 = null;
    318 
    319                 for (GpxTrack track : trackLayer.data.tracks) {
    320                         for (Collection<WayPoint> trackseg : track.trackSegs) {
    321                                 for (Iterator<WayPoint> it = trackseg.iterator(); it.hasNext();) {
    322                                         WayPoint w = it.next();
    323                                         if (audioTime < w.time) {
    324                                                 w2 = w;
    325                                                 break;
    326                                         }
    327                                         w1 = w;
    328                                 }
    329                                 if (w2 != null) break;
    330                         }
    331                         if (w2 != null) break;
    332                 }
    333                
    334                 if (w1 == null)
    335                         return;
    336                 eastNorth = w2 == null ?
    337                         w1.eastNorth :
    338                         w1.eastNorth.interpolate(w2.eastNorth,
    339                                         (audioTime - w1.time)/(w2.time - w1.time));
    340                 time = audioTime;
    341                 Main.map.mapView.repaint();
    342         }
     39    private Timer timer = null;
     40    private double animationInterval = 0.0; // seconds
     41    // private Rectangle audioTracer = null;
     42    // private Icon audioTracerIcon = null;
     43    static private PlayHeadMarker playHead = null;
     44    private MapMode oldMode = null;
     45    private EastNorth oldEastNorth;
     46    private boolean enabled;
     47    private boolean wasPlaying = false;
     48    private int dropTolerance = 50; /* pixels */
     49
     50    public static PlayHeadMarker create() {
     51        if (playHead == null) {
     52            try {
     53                playHead = new PlayHeadMarker();
     54            } catch (Exception ex) {
     55                return null;
     56            }
     57        }
     58        return playHead;
     59    }
     60
     61    private PlayHeadMarker() {
     62        super(new LatLon(0.0,0.0), "",
     63              Main.pref.get("marker.audiotracericon", "audio-tracer"),
     64              null, -1.0, 0.0);
     65        enabled = Main.pref.getBoolean("marker.traceaudio", true);
     66        if (! enabled) return;
     67        try { dropTolerance = Integer.parseInt(Main.pref.get("marker.playHeadDropTolerance", "50")); }
     68        catch(NumberFormatException x) { dropTolerance = 50; }
     69        Main.map.mapView.addMouseListener(new MouseAdapter() {
     70            @Override public void mousePressed(MouseEvent ev) {
     71                Point p = ev.getPoint();
     72                if (ev.getButton() != MouseEvent.BUTTON1 || p == null)
     73                    return;
     74                if (playHead.containsPoint(p)) {
     75                    /* when we get a click on the marker, we need to switch mode to avoid
     76                    * getting confused with other drag operations (like select) */
     77                    oldMode = Main.map.mapMode;
     78                    oldEastNorth = eastNorth;
     79                    PlayHeadDragMode playHeadDragMode = new PlayHeadDragMode(playHead);
     80                    Main.map.selectMapMode(playHeadDragMode);
     81                    playHeadDragMode.mousePressed(ev);
     82                }
     83            }
     84        });
     85    }
     86
     87    @Override public boolean containsPoint(Point p) {
     88        Point screen = Main.map.mapView.getPoint(eastNorth);
     89        Rectangle r = new Rectangle(screen.x, screen.y, symbol.getIconWidth(), symbol.getIconHeight());
     90        return r.contains(p);
     91    }
     92
     93    /**
     94     * called back from drag mode to say when we started dragging for real
     95    * (at least a short distance)
     96    */
     97    public void startDrag() {
     98        if (timer != null)
     99            timer.stop();
     100        wasPlaying = AudioPlayer.playing();
     101        if (wasPlaying) {
     102            try { AudioPlayer.pause(); }
     103            catch (Exception ex) { AudioPlayer.audioMalfunction(ex);}
     104        }
     105    }
     106
     107    /**
     108     * reinstate the old map mode after swuitching temporarily to do a play head drag
     109    */
     110    private void endDrag(boolean reset) {
     111        if (! wasPlaying || reset)
     112            try { AudioPlayer.pause(); }
     113            catch (Exception ex) { AudioPlayer.audioMalfunction(ex);}
     114        if (reset)
     115            eastNorth = oldEastNorth;
     116        Main.map.selectMapMode(oldMode);
     117        Main.map.mapView.repaint();
     118        timer.start();
     119    }
     120
     121    /**
     122    * apply the new position resulting from a drag in progress
     123     * @param en the new position in map terms
     124    */
     125    public void drag(EastNorth en) {
     126        eastNorth = en;
     127        Main.map.mapView.repaint();
     128    }
     129
     130    /**
     131     * Find the closest track point within the pixelTolerance of the screen point pNear
     132     * @param pNear : the point in screen coordinates near which to find a track point
     133    * @param pixelTolerance : only accept the point if within this number of pixels of en
     134    * @return the nearest trackpoint or null if nothing nearby
     135     *
     136    * XXX seems unused, F.R. 2008-03-15
     137    private WayPoint getClosestTrackPoint(Point pNear, double pixelTolerance) {
     138        WayPoint cw = null;
     139        AudioMarker recentlyPlayedMarker = AudioMarker.recentlyPlayedMarker();
     140        if (recentlyPlayedMarker != null) {
     141            // Find the track point closest to letting go of the play head
     142            double minDistance = pixelTolerance;
     143            GpxLayer trackLayer = recentlyPlayedMarker.parentLayer.fromLayer;
     144            if (trackLayer.data.tracks == null)
     145                return null;
     146
     147            for (GpxTrack track : trackLayer.data.tracks) {
     148                if (track.trackSegs == null)
     149                    continue;
     150
     151                for (Collection<WayPoint> trackseg : track.trackSegs) {
     152                    for (WayPoint w : trackseg) {
     153                        Point p = Main.map.mapView.getPoint(w.eastNorth);
     154                        double distance = p.distance(pNear);
     155                        if (distance <= minDistance) {
     156                            cw = w;
     157                            minDistance = distance;
     158                        }
     159                    }
     160                }
     161            }
     162        }
     163        return cw;
     164    }
     165    */
     166
     167    /**
     168    * reposition the play head at the point on the track nearest position given,
     169    * providing we are within reasonable distance from the track; otherwise reset to the
     170    * original position.
     171    * @param en the position to start looking from
     172    */
     173    public void reposition(EastNorth en) {
     174        // eastNorth = en;
     175        WayPoint cw = null;
     176        AudioMarker recent = AudioMarker.recentlyPlayedMarker();
     177        if (recent != null && recent.parentLayer != null && recent.parentLayer.fromLayer != null) {
     178            /* work out EastNorth equivalent of 50 (default) pixels tolerance */
     179            Point p = Main.map.mapView.getPoint(en);
     180            EastNorth enPlus25px = Main.map.mapView.getEastNorth(p.x+dropTolerance, p.y);
     181            cw = recent.parentLayer.fromLayer.nearestPointOnTrack(en, enPlus25px.east() - en.east());
     182        }
     183
     184        AudioMarker ca = null;
     185        /* Find the prior audio marker (there should always be one in the
     186         * layer, even if it is only one at the start of the track) to
     187         * offset the audio from */
     188        if (cw != null) {
     189            if (recent != null && recent.parentLayer != null) {
     190                for (Marker m : recent.parentLayer.data) {
     191                    if (m instanceof AudioMarker) {
     192                        AudioMarker a = (AudioMarker) m;
     193                        if (a.time > cw.time)
     194                            break;
     195                        ca = a;
     196                    }
     197                }
     198            }
     199        }
     200
     201        if (ca == null) {
     202            /* Not close enough to track, or no audio marker found for some other reason */
     203            JOptionPane.showMessageDialog(Main.parent, tr("You need to Drag the play head near to the GPX track whose associated sound track you were playing."));
     204            endDrag(true);
     205        } else {
     206            eastNorth = cw.eastNorth;
     207            ca.play(cw.time - ca.time);
     208            endDrag(false);
     209        }
     210    }
     211
     212    /**
     213     * Synchronize the audio at the position where the play head was paused before
     214     * dragging with the position on the track where it was dropped.
     215     * If this is quite near an audio marker, we use that
     216     * marker as the sync. location, otherwise we create a new marker at the
     217     * trackpoint nearest the end point of the drag point to apply the
     218    * sync to.
     219    * @param en : the EastNorth end point of the drag
     220    */
     221    public void synchronize(EastNorth en) {
     222        AudioMarker recent = AudioMarker.recentlyPlayedMarker();
     223        if(recent == null)
     224            return;
     225        /* First, see if we dropped onto an existing audio marker in the layer being played */
     226        Point startPoint = Main.map.mapView.getPoint(en);
     227        AudioMarker ca = null;
     228        if (recent.parentLayer != null) {
     229            double closestAudioMarkerDistanceSquared = 1.0E100;
     230            for (Marker m : recent.parentLayer.data) {
     231                if (m instanceof AudioMarker) {
     232                    double distanceSquared = m.eastNorth.distanceSq(en);
     233                    if (distanceSquared < closestAudioMarkerDistanceSquared) {
     234                        ca = (AudioMarker) m;
     235                        closestAudioMarkerDistanceSquared = distanceSquared;
     236                    }
     237                }
     238            }
     239        }
     240
     241        /* We found the closest marker: did we actually hit it? */
     242        if (ca != null && ! ca.containsPoint(startPoint)) ca = null;
     243
     244        /* If we didn't hit an audio marker, we need to create one at the nearest point on the track */
     245        if (ca == null) {
     246            /* work out EastNorth equivalent of 50 (default) pixels tolerance */
     247            Point p = Main.map.mapView.getPoint(en);
     248            EastNorth enPlus25px = Main.map.mapView.getEastNorth(p.x+dropTolerance, p.y);
     249            WayPoint cw = recent.parentLayer.fromLayer.nearestPointOnTrack(en, enPlus25px.east() - en.east());
     250            if (cw == null) {
     251                JOptionPane.showMessageDialog(Main.parent, tr("You need to SHIFT-Drag the play head onto an audio marker or onto the track point where you want to synchronize."));
     252                endDrag(true);
     253                return;
     254            }
     255            ca = recent.parentLayer.addAudioMarker(cw.time, cw.eastNorth);
     256        }
     257
     258        /* Actually do the synchronization */
     259        if(ca == null)
     260        {
     261            JOptionPane.showMessageDialog(Main.parent,tr("Unable to create new Audio marker."));
     262            endDrag(true);
     263        }
     264        else if (recent.parentLayer.synchronizeAudioMarkers(ca)) {
     265            JOptionPane.showMessageDialog(Main.parent, tr("Audio synchronized at point {0}.", ca.text));
     266            eastNorth = ca.eastNorth;
     267            endDrag(false);
     268        } else {
     269            JOptionPane.showMessageDialog(Main.parent,tr("Unable to synchronize in layer being played."));
     270            endDrag(true);
     271        }
     272    }
     273
     274    public void paint(Graphics g, MapView mv /*, boolean mousePressed */) {
     275        if (time < 0.0) return;
     276        Point screen = mv.getPoint(eastNorth);
     277        symbol.paintIcon(mv, g, screen.x, screen.y);
     278    }
     279
     280    public void animate() {
     281        if (! enabled) return;
     282        if (timer == null) {
     283            animationInterval = Double.parseDouble(Main.pref.get("marker.audioanimationinterval", "1")); //milliseconds
     284            timer = new Timer((int)(animationInterval * 1000.0), new ActionListener() {
     285                public void actionPerformed(ActionEvent e) {
     286                    timerAction();
     287                }
     288            });
     289            timer.setInitialDelay(0);
     290        } else {
     291            timer.stop();
     292        }
     293        timer.start();
     294    }
     295
     296    /**
     297     * callback for moving play head marker according to audio player position
     298    */
     299    public void timerAction() {
     300        AudioMarker recentlyPlayedMarker = AudioMarker.recentlyPlayedMarker();
     301        if (recentlyPlayedMarker == null)
     302            return;
     303        double audioTime = recentlyPlayedMarker.time +
     304            AudioPlayer.position() -
     305            recentlyPlayedMarker.offset -
     306            recentlyPlayedMarker.syncOffset;
     307        if (Math.abs(audioTime - time) < animationInterval)
     308            return;
     309        if (recentlyPlayedMarker.parentLayer == null) return;
     310        GpxLayer trackLayer = recentlyPlayedMarker.parentLayer.fromLayer;
     311        if (trackLayer == null)
     312            return;
     313        /* find the pair of track points for this position (adjusted by the syncOffset)
     314         * and interpolate between them
     315        */
     316        WayPoint w1 = null;
     317        WayPoint w2 = null;
     318
     319        for (GpxTrack track : trackLayer.data.tracks) {
     320            for (Collection<WayPoint> trackseg : track.trackSegs) {
     321                for (Iterator<WayPoint> it = trackseg.iterator(); it.hasNext();) {
     322                    WayPoint w = it.next();
     323                    if (audioTime < w.time) {
     324                        w2 = w;
     325                        break;
     326                    }
     327                    w1 = w;
     328                }
     329                if (w2 != null) break;
     330            }
     331            if (w2 != null) break;
     332        }
     333
     334        if (w1 == null)
     335            return;
     336        eastNorth = w2 == null ?
     337            w1.eastNorth :
     338            w1.eastNorth.interpolate(w2.eastNorth,
     339                    (audioTime - w1.time)/(w2.time - w1.time));
     340        time = audioTime;
     341        Main.map.mapView.repaint();
     342    }
    343343}
  • trunk/src/org/openstreetmap/josm/gui/layer/markerlayer/WebMarker.java

    r627 r1169  
    1515/**
    1616 * Marker class with Web URL activation.
    17  * 
     17 *
    1818 * @author Frederik Ramm <frederik@remote.org>
    1919 *
     
    2121public class WebMarker extends ButtonMarker {
    2222
    23         public URL webUrl;
     23    public URL webUrl;
    2424
    25         public static WebMarker create (LatLon ll, String url, MarkerLayer parentLayer, double time, double offset) {
    26                 try {
    27                         return new WebMarker(ll, new URL(url), parentLayer, time, offset);
    28                 } catch (Exception ex) {
    29                         return null;
    30                 }
    31         }
     25    public static WebMarker create (LatLon ll, String url, MarkerLayer parentLayer, double time, double offset) {
     26        try {
     27            return new WebMarker(ll, new URL(url), parentLayer, time, offset);
     28        } catch (Exception ex) {
     29            return null;
     30        }
     31    }
    3232
    33         private WebMarker(LatLon ll, URL webUrl, MarkerLayer parentLayer, double time, double offset) {
    34                 super(ll, "web.png", parentLayer, time, offset);
    35                 this.webUrl = webUrl;
    36         }
     33    private WebMarker(LatLon ll, URL webUrl, MarkerLayer parentLayer, double time, double offset) {
     34        super(ll, "web.png", parentLayer, time, offset);
     35        this.webUrl = webUrl;
     36    }
    3737
    38         @Override public void actionPerformed(ActionEvent ev) {
    39                 String error = OpenBrowser.displayUrl(webUrl.toString());
    40                 if (error != null) {
    41                         JOptionPane.showMessageDialog(Main.parent,
    42                                         "<html><b>" +
    43                                         tr("There was an error while trying to display the URL for this marker") +
    44                                         "</b><br>" + tr("(URL was: ") + webUrl.toString() + ")" + "<br>" + error,
    45                                         tr("Error displaying URL"), JOptionPane.ERROR_MESSAGE);
    46                 }
    47         }
     38    @Override public void actionPerformed(ActionEvent ev) {
     39        String error = OpenBrowser.displayUrl(webUrl.toString());
     40        if (error != null) {
     41            JOptionPane.showMessageDialog(Main.parent,
     42                    "<html><b>" +
     43                    tr("There was an error while trying to display the URL for this marker") +
     44                    "</b><br>" + tr("(URL was: ") + webUrl.toString() + ")" + "<br>" + error,
     45                    tr("Error displaying URL"), JOptionPane.ERROR_MESSAGE);
     46        }
     47    }
    4848}
  • trunk/src/org/openstreetmap/josm/gui/mappaint/AreaElemStyle.java

    r885 r1169  
    44public class AreaElemStyle extends ElemStyle
    55{
    6         public Color color;
    7         public LineElemStyle line = null;
     6    public Color color;
     7    public LineElemStyle line = null;
    88
    9         public AreaElemStyle (AreaElemStyle a, long maxScale, long minScale) {
    10                 this.color = a.color;
    11                 this.priority = a.priority;
    12                 this.maxScale = maxScale;
    13                 this.minScale = minScale;
    14         }
     9    public AreaElemStyle (AreaElemStyle a, long maxScale, long minScale) {
     10        this.color = a.color;
     11        this.priority = a.priority;
     12        this.maxScale = maxScale;
     13        this.minScale = minScale;
     14    }
    1515
    16         public AreaElemStyle(AreaElemStyle a, LineElemStyle l)
    17         {
    18                 this.color = a.color;
    19                 this.priority = a.priority;
    20                 this.maxScale = a.maxScale;
    21                 this.minScale = a.minScale;
    22                 this.line = l;
    23         }
     16    public AreaElemStyle(AreaElemStyle a, LineElemStyle l)
     17    {
     18        this.color = a.color;
     19        this.priority = a.priority;
     20        this.maxScale = a.maxScale;
     21        this.minScale = a.minScale;
     22        this.line = l;
     23    }
    2424
    25         public AreaElemStyle() { init(); }
     25    public AreaElemStyle() { init(); }
    2626
    27         public void init()
    28         {
    29                 color = null;
    30                 priority = 0;
    31         }
     27    public void init()
     28    {
     29        color = null;
     30        priority = 0;
     31    }
    3232}
  • trunk/src/org/openstreetmap/josm/gui/mappaint/ElemStyle.java

    r885 r1169  
    33abstract public class ElemStyle
    44{
    5         // zoom range to display the feature
    6         public long minScale;
    7         public long maxScale;
     5    // zoom range to display the feature
     6    public long minScale;
     7    public long maxScale;
    88
    9         public int priority;
     9    public int priority;
    1010}
    1111
  • trunk/src/org/openstreetmap/josm/gui/mappaint/ElemStyleHandler.java

    r999 r1169  
    1111public class ElemStyleHandler extends DefaultHandler
    1212{
    13         boolean inDoc, inRule, inCondition, inElemStyle, inLine, inLineMod, inIcon, inArea, inScaleMax, inScaleMin;
    14         boolean hadLine, hadLineMod, hadIcon, hadArea;
    15         ElemStyles styles;
    16         String styleName;
    17         RuleElem rule = new RuleElem();
    18 
    19         class RuleElem {
    20                 String key;
    21                 String value;
    22                 String boolValue;
    23                 long scaleMax;
    24                 long scaleMin;
    25                 LineElemStyle line = new LineElemStyle();
    26                 LineElemStyle linemod = new LineElemStyle();
    27                 AreaElemStyle area = new AreaElemStyle();
    28                 IconElemStyle icon = new IconElemStyle();
    29                 public void init()
    30                 {
    31                         key = value = boolValue = null;
    32                         scaleMax = 1000000000;
    33                         scaleMin = 0;
    34                         line.init();
    35                         linemod.init();
    36                         area.init();
    37                         icon.init();
    38                 }
    39         }
    40 
    41         public ElemStyleHandler(String name) {
    42                 styleName = name;
    43                 inDoc=inRule=inCondition=inElemStyle=inLine=inIcon=inArea=false;
    44                 rule.init();
    45                 styles = MapPaintStyles.getStyles();
    46         }
    47 
    48         Color convertColor(String colString)
    49         {
    50                 int i = colString.indexOf("#");
    51                 String colorString;
    52                 if(i < 0) // name only
    53                         colorString = Main.pref.get("color.mappaint."+styleName+"."+colString);
    54                 else if(i == 0) // value only
    55                         colorString = colString;
    56                 else // value and name
    57                         colorString = Main.pref.get("color.mappaint."+styleName+"."+colString.substring(0,i), colString.substring(i));
    58                 return ColorHelper.html2color(colorString);
    59         }
    60 
    61         @Override public void startDocument() {
    62                 inDoc = true;
    63         }
    64 
    65         @Override public void endDocument() {
    66                 inDoc = false;
    67         }
    68 
    69         @Override public void startElement(String uri,String name, String qName, Attributes atts) {
    70                 if (inDoc==true)
    71                 {
    72                         if (qName.equals("rule"))
    73                                 inRule=true;
    74                         else if (qName.equals("scale_max"))
    75                                 inScaleMax = true;
    76                         else if (qName.equals("scale_min"))
    77                                 inScaleMin = true;
    78                         else if (qName.equals("condition") && inRule)
    79                         {
    80                                 inCondition=true;
    81                                 for (int count=0; count<atts.getLength(); count++)
    82                                 {
    83                                         if(atts.getQName(count).equals("k"))
    84                                                 rule.key = atts.getValue(count);
    85                                         else if(atts.getQName(count).equals("v"))
    86                                                 rule.value = atts.getValue(count);
    87                                         else if(atts.getQName(count).equals("b"))
    88                                                 rule.boolValue = atts.getValue(count);
    89                                 }
    90                         }
    91                         else if (qName.equals("line"))
    92                         {
    93                                 hadLine = inLine = true;
    94                                 for (int count=0; count<atts.getLength(); count++)
    95                                 {
    96                                         if(atts.getQName(count).equals("width"))
    97                                                 rule.line.width = Integer.parseInt(atts.getValue(count));
    98                                         else if (atts.getQName(count).equals("colour"))
    99                                                 rule.line.color=convertColor(atts.getValue(count));
    100                                         else if (atts.getQName(count).equals("realwidth"))
    101                                                 rule.line.realWidth=Integer.parseInt(atts.getValue(count));
    102                                         else if (atts.getQName(count).equals("dashed"))
    103                                                 rule.line.dashed=Boolean.parseBoolean(atts.getValue(count));
    104                                         else if(atts.getQName(count).equals("priority"))
    105                                                 rule.line.priority = Integer.parseInt(atts.getValue(count));
    106                                 }
    107                         }
    108                         else if (qName.equals("linemod"))
    109                         {
    110                                 hadLineMod = inLineMod = true;
    111                                 for (int count=0; count<atts.getLength(); count++)
    112                                 {
    113                                         if(atts.getQName(count).equals("width"))
    114                                         {
    115                                                 String val = atts.getValue(count);
    116                                                 if(val.startsWith("+"))
    117                                                 {
    118                                                         rule.linemod.width = Integer.parseInt(val.substring(1));
    119                                                         rule.linemod.widthMode = LineElemStyle.WidthMode.OFFSET;
    120                                                 }
    121                                                 else if(val.startsWith("-"))
    122                                                 {
    123                                                         rule.linemod.width = Integer.parseInt(val);
    124                                                         rule.linemod.widthMode = LineElemStyle.WidthMode.OFFSET;
    125                                                 }
    126                                                 else if(val.endsWith("%"))
    127                                                 {
    128                                                         rule.linemod.width = Integer.parseInt(val.substring(0, val.length()-1));
    129                                                         rule.linemod.widthMode = LineElemStyle.WidthMode.PERCENT;
    130                                                 }
    131                                                 else
    132                                                         rule.linemod.width = Integer.parseInt(val);
    133                                         }
    134                                         else if (atts.getQName(count).equals("colour"))
    135                                                 rule.linemod.color=convertColor(atts.getValue(count));
    136                                         else if (atts.getQName(count).equals("realwidth"))
    137                                                 rule.linemod.realWidth=Integer.parseInt(atts.getValue(count));
    138                                         else if (atts.getQName(count).equals("dashed"))
    139                                                 rule.linemod.dashed=Boolean.parseBoolean(atts.getValue(count));
    140                                         else if(atts.getQName(count).equals("priority"))
    141                                                 rule.linemod.priority = Integer.parseInt(atts.getValue(count));
    142                                         else if(atts.getQName(count).equals("mode"))
    143                                                 rule.linemod.over = !atts.getValue(count).equals("under");
    144                                 }
    145                         }
    146                         else if (qName.equals("icon"))
    147                         {
    148                                 hadIcon = inIcon = true;
    149                                 for (int count=0; count<atts.getLength(); count++)
    150                                 {
    151                                         if (atts.getQName(count).equals("src"))
    152                                                 rule.icon.icon = MapPaintStyles.getIcon(atts.getValue(count), styleName);
    153                                         else if (atts.getQName(count).equals("annotate"))
    154                                                 rule.icon.annotate = Boolean.parseBoolean (atts.getValue(count));
    155                                         else if(atts.getQName(count).equals("priority"))
    156                                                 rule.icon.priority = Integer.parseInt(atts.getValue(count));
    157                                 }
    158                         }
    159                         else if (qName.equals("area"))
    160                         {
    161                                 hadArea = inArea = true;
    162                                 for (int count=0; count<atts.getLength(); count++)
    163                                 {
    164                                         if (atts.getQName(count).equals("colour"))
    165                                                 rule.area.color=convertColor(atts.getValue(count));
    166                                         else if(atts.getQName(count).equals("priority"))
    167                                                 rule.area.priority = Integer.parseInt(atts.getValue(count));
    168                                 }
    169                         }
    170                 }
    171         }
    172 
    173         @Override public void endElement(String uri,String name, String qName)
    174         {
    175                 if (inRule && qName.equals("rule"))
    176                 {
    177                         if(hadLine)
    178                                 styles.add(styleName, rule.key, rule.value, rule.boolValue,
    179                                 new LineElemStyle(rule.line, rule.scaleMax, rule.scaleMin));
    180                         if(hadLineMod)
    181                                 styles.addModifier(styleName, rule.key, rule.value, rule.boolValue,
    182                                 new LineElemStyle(rule.linemod, rule.scaleMax, rule.scaleMin));
    183                         if(hadIcon)
    184                                 styles.add(styleName, rule.key, rule.value, rule.boolValue,
    185                                 new IconElemStyle(rule.icon, rule.scaleMax, rule.scaleMin));
    186                         if(hadArea)
    187                                 styles.add(styleName, rule.key, rule.value, rule.boolValue,
    188                                 new AreaElemStyle(rule.area, rule.scaleMax, rule.scaleMin));
    189                         inRule = false;
    190                         hadLine = hadLineMod = hadIcon = hadArea = false;
    191                         rule.init();
    192                 }
    193                 else if (inCondition && qName.equals("condition"))
    194                         inCondition = false;
    195                 else if (inLine && qName.equals("line"))
    196                         inLine = false;
    197                 else if (inLineMod && qName.equals("linemod"))
    198                         inLineMod = false;
    199                 else if (inIcon && qName.equals("icon"))
    200                         inIcon = false;
    201                 else if (inArea && qName.equals("area"))
    202                         inArea = false;
    203                 else if (qName.equals("scale_max"))
    204                         inScaleMax = false;
    205                 else if (qName.equals("scale_min"))
    206                         inScaleMin = false;
    207         }
    208 
    209         @Override public void characters(char ch[], int start, int length)
    210         {
    211                 if (inScaleMax == true)
    212                         rule.scaleMax = Long.parseLong(new String(ch, start, length));
    213                 else if (inScaleMin == true)
    214                         rule.scaleMin = Long.parseLong(new String(ch, start, length));
    215         }
     13    boolean inDoc, inRule, inCondition, inElemStyle, inLine, inLineMod, inIcon, inArea, inScaleMax, inScaleMin;
     14    boolean hadLine, hadLineMod, hadIcon, hadArea;
     15    ElemStyles styles;
     16    String styleName;
     17    RuleElem rule = new RuleElem();
     18
     19    class RuleElem {
     20        String key;
     21        String value;
     22        String boolValue;
     23        long scaleMax;
     24        long scaleMin;
     25        LineElemStyle line = new LineElemStyle();
     26        LineElemStyle linemod = new LineElemStyle();
     27        AreaElemStyle area = new AreaElemStyle();
     28        IconElemStyle icon = new IconElemStyle();
     29        public void init()
     30        {
     31            key = value = boolValue = null;
     32            scaleMax = 1000000000;
     33            scaleMin = 0;
     34            line.init();
     35            linemod.init();
     36            area.init();
     37            icon.init();
     38        }
     39    }
     40
     41    public ElemStyleHandler(String name) {
     42        styleName = name;
     43        inDoc=inRule=inCondition=inElemStyle=inLine=inIcon=inArea=false;
     44        rule.init();
     45        styles = MapPaintStyles.getStyles();
     46    }
     47
     48    Color convertColor(String colString)
     49    {
     50        int i = colString.indexOf("#");
     51        String colorString;
     52        if(i < 0) // name only
     53            colorString = Main.pref.get("color.mappaint."+styleName+"."+colString);
     54        else if(i == 0) // value only
     55            colorString = colString;
     56        else // value and name
     57            colorString = Main.pref.get("color.mappaint."+styleName+"."+colString.substring(0,i), colString.substring(i));
     58        return ColorHelper.html2color(colorString);
     59    }
     60
     61    @Override public void startDocument() {
     62        inDoc = true;
     63    }
     64
     65    @Override public void endDocument() {
     66        inDoc = false;
     67    }
     68
     69    @Override public void startElement(String uri,String name, String qName, Attributes atts) {
     70        if (inDoc==true)
     71        {
     72            if (qName.equals("rule"))
     73                inRule=true;
     74            else if (qName.equals("scale_max"))
     75                inScaleMax = true;
     76            else if (qName.equals("scale_min"))
     77                inScaleMin = true;
     78            else if (qName.equals("condition") && inRule)
     79            {
     80                inCondition=true;
     81                for (int count=0; count<atts.getLength(); count++)
     82                {
     83                    if(atts.getQName(count).equals("k"))
     84                        rule.key = atts.getValue(count);
     85                    else if(atts.getQName(count).equals("v"))
     86                        rule.value = atts.getValue(count);
     87                    else if(atts.getQName(count).equals("b"))
     88                        rule.boolValue = atts.getValue(count);
     89                }
     90            }
     91            else if (qName.equals("line"))
     92            {
     93                hadLine = inLine = true;
     94                for (int count=0; count<atts.getLength(); count++)
     95                {
     96                    if(atts.getQName(count).equals("width"))
     97                        rule.line.width = Integer.parseInt(atts.getValue(count));
     98                    else if (atts.getQName(count).equals("colour"))
     99                        rule.line.color=convertColor(atts.getValue(count));
     100                    else if (atts.getQName(count).equals("realwidth"))
     101                        rule.line.realWidth=Integer.parseInt(atts.getValue(count));
     102                    else if (atts.getQName(count).equals("dashed"))
     103                        rule.line.dashed=Boolean.parseBoolean(atts.getValue(count));
     104                    else if(atts.getQName(count).equals("priority"))
     105                        rule.line.priority = Integer.parseInt(atts.getValue(count));
     106                }
     107            }
     108            else if (qName.equals("linemod"))
     109            {
     110                hadLineMod = inLineMod = true;
     111                for (int count=0; count<atts.getLength(); count++)
     112                {
     113                    if(atts.getQName(count).equals("width"))
     114                    {
     115                        String val = atts.getValue(count);
     116                        if(val.startsWith("+"))
     117                        {
     118                            rule.linemod.width = Integer.parseInt(val.substring(1));
     119                            rule.linemod.widthMode = LineElemStyle.WidthMode.OFFSET;
     120                        }
     121                        else if(val.startsWith("-"))
     122                        {
     123                            rule.linemod.width = Integer.parseInt(val);
     124                            rule.linemod.widthMode = LineElemStyle.WidthMode.OFFSET;
     125                        }
     126                        else if(val.endsWith("%"))
     127                        {
     128                            rule.linemod.width = Integer.parseInt(val.substring(0, val.length()-1));
     129                            rule.linemod.widthMode = LineElemStyle.WidthMode.PERCENT;
     130                        }
     131                        else
     132                            rule.linemod.width = Integer.parseInt(val);
     133                    }
     134                    else if (atts.getQName(count).equals("colour"))
     135                        rule.linemod.color=convertColor(atts.getValue(count));
     136                    else if (atts.getQName(count).equals("realwidth"))
     137                        rule.linemod.realWidth=Integer.parseInt(atts.getValue(count));
     138                    else if (atts.getQName(count).equals("dashed"))
     139                        rule.linemod.dashed=Boolean.parseBoolean(atts.getValue(count));
     140                    else if(atts.getQName(count).equals("priority"))
     141                        rule.linemod.priority = Integer.parseInt(atts.getValue(count));
     142                    else if(atts.getQName(count).equals("mode"))
     143                        rule.linemod.over = !atts.getValue(count).equals("under");
     144                }
     145            }
     146            else if (qName.equals("icon"))
     147            {
     148                hadIcon = inIcon = true;
     149                for (int count=0; count<atts.getLength(); count++)
     150                {
     151                    if (atts.getQName(count).equals("src"))
     152                        rule.icon.icon = MapPaintStyles.getIcon(atts.getValue(count), styleName);
     153                    else if (atts.getQName(count).equals("annotate"))
     154                        rule.icon.annotate = Boolean.parseBoolean (atts.getValue(count));
     155                    else if(atts.getQName(count).equals("priority"))
     156                        rule.icon.priority = Integer.parseInt(atts.getValue(count));
     157                }
     158            }
     159            else if (qName.equals("area"))
     160            {
     161                hadArea = inArea = true;
     162                for (int count=0; count<atts.getLength(); count++)
     163                {
     164                    if (atts.getQName(count).equals("colour"))
     165                        rule.area.color=convertColor(atts.getValue(count));
     166                    else if(atts.getQName(count).equals("priority"))
     167                        rule.area.priority = Integer.parseInt(atts.getValue(count));
     168                }
     169            }
     170        }
     171    }
     172
     173    @Override public void endElement(String uri,String name, String qName)
     174    {
     175        if (inRule && qName.equals("rule"))
     176        {
     177            if(hadLine)
     178                styles.add(styleName, rule.key, rule.value, rule.boolValue,
     179                new LineElemStyle(rule.line, rule.scaleMax, rule.scaleMin));
     180            if(hadLineMod)
     181                styles.addModifier(styleName, rule.key, rule.value, rule.boolValue,
     182                new LineElemStyle(rule.linemod, rule.scaleMax, rule.scaleMin));
     183            if(hadIcon)
     184                styles.add(styleName, rule.key, rule.value, rule.boolValue,
     185                new IconElemStyle(rule.icon, rule.scaleMax, rule.scaleMin));
     186            if(hadArea)
     187                styles.add(styleName, rule.key, rule.value, rule.boolValue,
     188                new AreaElemStyle(rule.area, rule.scaleMax, rule.scaleMin));
     189            inRule = false;
     190            hadLine = hadLineMod = hadIcon = hadArea = false;
     191            rule.init();
     192        }
     193        else if (inCondition && qName.equals("condition"))
     194            inCondition = false;
     195        else if (inLine && qName.equals("line"))
     196            inLine = false;
     197        else if (inLineMod && qName.equals("linemod"))
     198            inLineMod = false;
     199        else if (inIcon && qName.equals("icon"))
     200            inIcon = false;
     201        else if (inArea && qName.equals("area"))
     202            inArea = false;
     203        else if (qName.equals("scale_max"))
     204            inScaleMax = false;
     205        else if (qName.equals("scale_min"))
     206            inScaleMin = false;
     207    }
     208
     209    @Override public void characters(char ch[], int start, int length)
     210    {
     211        if (inScaleMax == true)
     212            rule.scaleMax = Long.parseLong(new String(ch, start, length));
     213        else if (inScaleMin == true)
     214            rule.scaleMin = Long.parseLong(new String(ch, start, length));
     215    }
    216216}
  • trunk/src/org/openstreetmap/josm/gui/mappaint/ElemStyles.java

    r999 r1169  
    1414public class ElemStyles
    1515{
    16         private class StyleSet {
    17                 HashMap<String, IconElemStyle> icons;
    18                 HashMap<String, LineElemStyle> lines;
    19                 HashMap<String, AreaElemStyle> areas;
    20                 HashMap<String, LineElemStyle> modifiers;
    21                 public StyleSet()
    22                 {
    23                         icons = new HashMap<String, IconElemStyle>();
    24                         lines = new HashMap<String, LineElemStyle>();
    25                         modifiers = new HashMap<String, LineElemStyle>();
    26                         areas = new HashMap<String, AreaElemStyle>();
    27                 }
    28         }
    29         HashMap<String, StyleSet> styleSet;
     16    private class StyleSet {
     17        HashMap<String, IconElemStyle> icons;
     18        HashMap<String, LineElemStyle> lines;
     19        HashMap<String, AreaElemStyle> areas;
     20        HashMap<String, LineElemStyle> modifiers;
     21        public StyleSet()
     22        {
     23            icons = new HashMap<String, IconElemStyle>();
     24            lines = new HashMap<String, LineElemStyle>();
     25            modifiers = new HashMap<String, LineElemStyle>();
     26            areas = new HashMap<String, AreaElemStyle>();
     27        }
     28    }
     29    HashMap<String, StyleSet> styleSet;
    3030
    31         public ElemStyles()
    32         {
    33                 styleSet = new HashMap<String, StyleSet>();
    34         }
     31    public ElemStyles()
     32    {
     33        styleSet = new HashMap<String, StyleSet>();
     34    }
    3535
    36         private String getKey(String k, String v, String b)
    37         {
    38                 if(v != null)
    39                         return "n" + k + "=" + v;
    40                 else if(b != null)
    41                         return "b" + k  + "=" + OsmUtils.getNamedOsmBoolean(b);
    42                 else
    43                         return "x" + k;
    44         }
     36    private String getKey(String k, String v, String b)
     37    {
     38        if(v != null)
     39            return "n" + k + "=" + v;
     40        else if(b != null)
     41            return "b" + k  + "=" + OsmUtils.getNamedOsmBoolean(b);
     42        else
     43            return "x" + k;
     44    }
    4545
    46         public void add(String name, String k, String v, String b, LineElemStyle style)
    47         {
    48                 getStyleSet(name, true).lines.put(getKey(k,v,b), style);
    49         }
     46    public void add(String name, String k, String v, String b, LineElemStyle style)
     47    {
     48        getStyleSet(name, true).lines.put(getKey(k,v,b), style);
     49    }
    5050
    51         public void addModifier(String name, String k, String v, String b, LineElemStyle style)
    52         {
    53                 getStyleSet(name, true).modifiers.put(getKey(k,v,b), style);
    54         }
     51    public void addModifier(String name, String k, String v, String b, LineElemStyle style)
     52    {
     53        getStyleSet(name, true).modifiers.put(getKey(k,v,b), style);
     54    }
    5555
    56         public void add(String name, String k, String v, String b, AreaElemStyle style)
    57         {
    58                 getStyleSet(name, true).areas.put(getKey(k,v,b), style);
    59         }
     56    public void add(String name, String k, String v, String b, AreaElemStyle style)
     57    {
     58        getStyleSet(name, true).areas.put(getKey(k,v,b), style);
     59    }
    6060
    61         public void add(String name, String k, String v, String b, IconElemStyle style)
    62         {
    63                 getStyleSet(name, true).icons.put(getKey(k,v,b), style);
    64         }
     61    public void add(String name, String k, String v, String b, IconElemStyle style)
     62    {
     63        getStyleSet(name, true).icons.put(getKey(k,v,b), style);
     64    }
    6565
    66         private StyleSet getStyleSet(String name, boolean create)
    67         {
    68                 if(name == null)
    69                         name = Main.pref.get("mappaint.style", "standard");
    70                 StyleSet s = styleSet.get(name);
    71                 if(create && s == null)
    72                 {
    73                         s = new StyleSet();
    74                         styleSet.put(name, s);
    75                 }
    76                 return s;
    77         }
     66    private StyleSet getStyleSet(String name, boolean create)
     67    {
     68        if(name == null)
     69            name = Main.pref.get("mappaint.style", "standard");
     70        StyleSet s = styleSet.get(name);
     71        if(create && s == null)
     72        {
     73            s = new StyleSet();
     74            styleSet.put(name, s);
     75        }
     76        return s;
     77    }
    7878
    79         public IconElemStyle get(Node n)
    80         {
    81                 StyleSet ss = getStyleSet(null, false);
    82                 IconElemStyle ret = null;
    83                 if(ss != null && n.keys != null)
    84                 {
    85                         Iterator<String> iterator = n.keys.keySet().iterator();
    86                         while(iterator.hasNext())
    87                         {
    88                                 String key = iterator.next();
    89                                 String val = n.keys.get(key);
    90                                 IconElemStyle style;
    91                                 if((style = ss.icons.get("n" + key + "=" + val)) != null)
    92                                 {
    93                                         if(ret == null || style.priority > ret.priority)
    94                                                 ret = style;
    95                                 }
    96                                 if((style = ss.icons.get("b" + key + "=" + OsmUtils.getNamedOsmBoolean(val))) != null)
    97                                 {
    98                                         if(ret == null || style.priority > ret.priority)
    99                                                 ret = style;
    100                                 }
    101                                 if((style = ss.icons.get("x" + key)) != null)
    102                                 {
    103                                         if(ret == null || style.priority > ret.priority)
    104                                                 ret = style;
    105                                 }
    106                         }
    107                 }
    108                 return ret;
    109         }
     79    public IconElemStyle get(Node n)
     80    {
     81        StyleSet ss = getStyleSet(null, false);
     82        IconElemStyle ret = null;
     83        if(ss != null && n.keys != null)
     84        {
     85            Iterator<String> iterator = n.keys.keySet().iterator();
     86            while(iterator.hasNext())
     87            {
     88                String key = iterator.next();
     89                String val = n.keys.get(key);
     90                IconElemStyle style;
     91                if((style = ss.icons.get("n" + key + "=" + val)) != null)
     92                {
     93                    if(ret == null || style.priority > ret.priority)
     94                        ret = style;
     95                }
     96                if((style = ss.icons.get("b" + key + "=" + OsmUtils.getNamedOsmBoolean(val))) != null)
     97                {
     98                    if(ret == null || style.priority > ret.priority)
     99                        ret = style;
     100                }
     101                if((style = ss.icons.get("x" + key)) != null)
     102                {
     103                    if(ret == null || style.priority > ret.priority)
     104                        ret = style;
     105                }
     106            }
     107        }
     108        return ret;
     109    }
    110110
    111         public ElemStyle get(Way w)
    112         {
    113                 StyleSet ss = getStyleSet(null, false);
    114                 if(ss == null || w.keys == null)
    115                         return null;
    116                 AreaElemStyle retArea = null;
    117                 LineElemStyle retLine = null;
    118                 String linestring = null;
    119                 HashMap<String, LineElemStyle> over = new HashMap<String, LineElemStyle>();
    120                 Iterator<String> iterator = w.keys.keySet().iterator();
    121                 while(iterator.hasNext())
    122                 {
    123                         String key = iterator.next();
    124                         String val = w.keys.get(key);
    125                         AreaElemStyle styleArea;
    126                         LineElemStyle styleLine;
    127                         String idx = "n" + key + "=" + val;
    128                         if((styleArea = ss.areas.get(idx)) != null && (retArea == null || styleArea.priority > retArea.priority))
    129                                 retArea = styleArea;
    130                         if((styleLine = ss.lines.get(idx)) != null && (retLine == null || styleLine.priority > retLine.priority))
    131                         {
    132                                 retLine = styleLine;
    133                                 linestring = idx;
    134                         }
    135                         if((styleLine = ss.modifiers.get(idx)) != null)
    136                                 over.put(idx, styleLine);
    137                         idx = "b" + key + "=" + OsmUtils.getNamedOsmBoolean(val);
    138                         if((styleArea = ss.areas.get(idx)) != null && (retArea == null || styleArea.priority > retArea.priority))
    139                                 retArea = styleArea;
    140                         if((styleLine = ss.lines.get(idx)) != null && (retLine == null || styleLine.priority > retLine.priority))
    141                         {
    142                                 retLine = styleLine;
    143                                 linestring = idx;
    144                         }
    145                         if((styleLine = ss.modifiers.get(idx)) != null)
    146                                 over.put(idx, styleLine);
    147                         idx = "x" + key;
    148                         if((styleArea = ss.areas.get(idx)) != null && (retArea == null || styleArea.priority > retArea.priority))
    149                                 retArea = styleArea;
    150                         if((styleLine = ss.lines.get(idx)) != null && (retLine == null || styleLine.priority > retLine.priority))
    151                         {
    152                                 retLine = styleLine;
    153                                 linestring = idx;
    154                         }
    155                         if((styleLine = ss.modifiers.get(idx)) != null)
    156                                 over.put(idx, styleLine);
    157                 }
    158                 over.remove(linestring);
    159                 if(over.size() != 0 && retLine != null)
    160                 {
    161                         List<LineElemStyle> s = new LinkedList<LineElemStyle>(over.values());
    162                         Collections.sort(s);
    163                         retLine = new LineElemStyle(retLine, s);
    164                 }
    165                 if(retArea != null)
    166                 {
    167                         if(retLine != null)
    168                                 return new AreaElemStyle(retArea, retLine);
    169                         else
    170                                 return retArea;
    171                 }
    172                 return retLine;
    173         }
     111    public ElemStyle get(Way w)
     112    {
     113        StyleSet ss = getStyleSet(null, false);
     114        if(ss == null || w.keys == null)
     115            return null;
     116        AreaElemStyle retArea = null;
     117        LineElemStyle retLine = null;
     118        String linestring = null;
     119        HashMap<String, LineElemStyle> over = new HashMap<String, LineElemStyle>();
     120        Iterator<String> iterator = w.keys.keySet().iterator();
     121        while(iterator.hasNext())
     122        {
     123            String key = iterator.next();
     124            String val = w.keys.get(key);
     125            AreaElemStyle styleArea;
     126            LineElemStyle styleLine;
     127            String idx = "n" + key + "=" + val;
     128            if((styleArea = ss.areas.get(idx)) != null && (retArea == null || styleArea.priority > retArea.priority))
     129                retArea = styleArea;
     130            if((styleLine = ss.lines.get(idx)) != null && (retLine == null || styleLine.priority > retLine.priority))
     131            {
     132                retLine = styleLine;
     133                linestring = idx;
     134            }
     135            if((styleLine = ss.modifiers.get(idx)) != null)
     136                over.put(idx, styleLine);
     137            idx = "b" + key + "=" + OsmUtils.getNamedOsmBoolean(val);
     138            if((styleArea = ss.areas.get(idx)) != null && (retArea == null || styleArea.priority > retArea.priority))
     139                retArea = styleArea;
     140            if((styleLine = ss.lines.get(idx)) != null && (retLine == null || styleLine.priority > retLine.priority))
     141            {
     142                retLine = styleLine;
     143                linestring = idx;
     144            }
     145            if((styleLine = ss.modifiers.get(idx)) != null)
     146                over.put(idx, styleLine);
     147            idx = "x" + key;
     148            if((styleArea = ss.areas.get(idx)) != null && (retArea == null || styleArea.priority > retArea.priority))
     149                retArea = styleArea;
     150            if((styleLine = ss.lines.get(idx)) != null && (retLine == null || styleLine.priority > retLine.priority))
     151            {
     152                retLine = styleLine;
     153                linestring = idx;
     154            }
     155            if((styleLine = ss.modifiers.get(idx)) != null)
     156                over.put(idx, styleLine);
     157        }
     158        over.remove(linestring);
     159        if(over.size() != 0 && retLine != null)
     160        {
     161            List<LineElemStyle> s = new LinkedList<LineElemStyle>(over.values());
     162            Collections.sort(s);
     163            retLine = new LineElemStyle(retLine, s);
     164        }
     165        if(retArea != null)
     166        {
     167            if(retLine != null)
     168                return new AreaElemStyle(retArea, retLine);
     169            else
     170                return retArea;
     171        }
     172        return retLine;
     173    }
    174174
    175         public boolean isArea(Way w)
    176         {
    177                 StyleSet ss = getStyleSet(null, false);
    178                 if(ss != null && w.keys != null)
    179                 {
    180                         Iterator<String> iterator = w.keys.keySet().iterator();
    181                         while(iterator.hasNext())
    182                         {
    183                                 String key = iterator.next();
    184                                 String val = w.keys.get(key);
    185                                 if(ss.areas.containsKey("n" + key + "=" + val)
    186                                 || ss.areas.containsKey("n" + key + "=" + OsmUtils.getNamedOsmBoolean(val))
    187                                 || ss.areas.containsKey("x" + key))
    188                                         return true;
    189                         }
    190                 }
    191                 return false;
    192         }
    193         public boolean hasAreas()
    194         {
    195                 StyleSet ss = getStyleSet(null, false);
    196                 return ss != null && ss.areas.size() > 0;
    197         }
     175    public boolean isArea(Way w)
     176    {
     177        StyleSet ss = getStyleSet(null, false);
     178        if(ss != null && w.keys != null)
     179        {
     180            Iterator<String> iterator = w.keys.keySet().iterator();
     181            while(iterator.hasNext())
     182            {
     183                String key = iterator.next();
     184                String val = w.keys.get(key);
     185                if(ss.areas.containsKey("n" + key + "=" + val)
     186                || ss.areas.containsKey("n" + key + "=" + OsmUtils.getNamedOsmBoolean(val))
     187                || ss.areas.containsKey("x" + key))
     188                    return true;
     189            }
     190        }
     191        return false;
     192    }
     193    public boolean hasAreas()
     194    {
     195        StyleSet ss = getStyleSet(null, false);
     196        return ss != null && ss.areas.size() > 0;
     197    }
    198198}
  • trunk/src/org/openstreetmap/josm/gui/mappaint/IconElemStyle.java

    r885 r1169  
    44public class IconElemStyle extends ElemStyle
    55{
    6         public ImageIcon icon;
    7         public boolean annotate;
     6    public ImageIcon icon;
     7    public boolean annotate;
    88
    9         public IconElemStyle (IconElemStyle i, long maxScale, long minScale) {
    10                 this.icon = i.icon;
    11                 this.annotate = i.annotate;
    12                 this.priority = i.priority;
    13                 this.maxScale = maxScale;
    14                 this.minScale = minScale;
    15         }
    16         public IconElemStyle() { init(); }
     9    public IconElemStyle (IconElemStyle i, long maxScale, long minScale) {
     10        this.icon = i.icon;
     11        this.annotate = i.annotate;
     12        this.priority = i.priority;
     13        this.maxScale = maxScale;
     14        this.minScale = minScale;
     15    }
     16    public IconElemStyle() { init(); }
    1717
    18         public void init()
    19         {
    20                 icon = null;
    21                 priority = 0;
    22                 annotate = true;
    23         }
     18    public void init()
     19    {
     20        icon = null;
     21        priority = 0;
     22        annotate = true;
     23    }
    2424}
  • trunk/src/org/openstreetmap/josm/gui/mappaint/LineElemStyle.java

    r999 r1169  
    66public class LineElemStyle extends ElemStyle implements Comparable<LineElemStyle>
    77{
    8         public int width;
    9         public int realWidth; //the real width of this line in meter
    10         public Color color;
    11         public boolean dashed;
     8    public int width;
     9    public int realWidth; //the real width of this line in meter
     10    public Color color;
     11    public boolean dashed;
    1212
    13         public boolean over;
    14         public enum WidthMode { ABSOLUTE, PERCENT, OFFSET }
    15         public WidthMode widthMode;
     13    public boolean over;
     14    public enum WidthMode { ABSOLUTE, PERCENT, OFFSET }
     15    public WidthMode widthMode;
    1616
    17         public Collection<LineElemStyle> overlays;
     17    public Collection<LineElemStyle> overlays;
    1818
    19         public LineElemStyle(LineElemStyle s, long maxScale, long minScale) {
    20                 this.width = s.width;
    21                 this.realWidth = s.realWidth;
    22                 this.color = s.color;
    23                 this.dashed = s.dashed;
    24                 this.over = s.over;
    25                 this.widthMode = s.widthMode;
     19    public LineElemStyle(LineElemStyle s, long maxScale, long minScale) {
     20        this.width = s.width;
     21        this.realWidth = s.realWidth;
     22        this.color = s.color;
     23        this.dashed = s.dashed;
     24        this.over = s.over;
     25        this.widthMode = s.widthMode;
    2626
    27                 this.priority = s.priority;
    28                 this.maxScale = maxScale;
    29                 this.minScale = minScale;
    30         }
     27        this.priority = s.priority;
     28        this.maxScale = maxScale;
     29        this.minScale = minScale;
     30    }
    3131
    32         public LineElemStyle(LineElemStyle s, Collection<LineElemStyle> overlays) {
    33                 this.width = s.width;
    34                 this.realWidth = s.realWidth;
    35                 this.color = s.color;
    36                 this.dashed = s.dashed;
    37                 this.over = s.over;
    38                 this.widthMode = s.widthMode;
     32    public LineElemStyle(LineElemStyle s, Collection<LineElemStyle> overlays) {
     33        this.width = s.width;
     34        this.realWidth = s.realWidth;
     35        this.color = s.color;
     36        this.dashed = s.dashed;
     37        this.over = s.over;
     38        this.widthMode = s.widthMode;
    3939
    40                 this.priority = s.priority;
    41                 this.maxScale = s.maxScale;
    42                 this.minScale = s.minScale;
     40        this.priority = s.priority;
     41        this.maxScale = s.maxScale;
     42        this.minScale = s.minScale;
    4343
    44                 this.overlays = overlays;
    45         }
     44        this.overlays = overlays;
     45    }
    4646
    47         public LineElemStyle() { init(); }
     47    public LineElemStyle() { init(); }
    4848
    49         public void init()
    50         {
    51                 width = 1;
    52                 realWidth = 0;
    53                 dashed = false;
    54                 priority = 0;
    55                 color = null;
    56                 over = true; // only used for line modifications
    57                 widthMode = WidthMode.ABSOLUTE;
    58                 overlays = null;
    59         }
     49    public void init()
     50    {
     51        width = 1;
     52        realWidth = 0;
     53        dashed = false;
     54        priority = 0;
     55        color = null;
     56        over = true; // only used for line modifications
     57        widthMode = WidthMode.ABSOLUTE;
     58        overlays = null;
     59    }
    6060
    61         // get width for overlays
    62         public int getWidth(int ref)
    63         {
    64                 int res;
    65                 if(widthMode == WidthMode.ABSOLUTE)
    66                         res = width;
    67                 else if(widthMode == WidthMode.OFFSET)
    68                         res = ref + width;
    69                 else
    70                 {
    71                         if(width < 0)
    72                                 res = 0;
    73                         else
    74                                 res = ref*width/100;
    75                 }
    76                 return res <= 0 ? 1 : res;
    77         }
     61    // get width for overlays
     62    public int getWidth(int ref)
     63    {
     64        int res;
     65        if(widthMode == WidthMode.ABSOLUTE)
     66            res = width;
     67        else if(widthMode == WidthMode.OFFSET)
     68            res = ref + width;
     69        else
     70        {
     71            if(width < 0)
     72                res = 0;
     73            else
     74                res = ref*width/100;
     75        }
     76        return res <= 0 ? 1 : res;
     77    }
    7878
    79         public int compareTo(LineElemStyle s)
    80         {
    81                 if(s.priority != priority)
    82                         return s.priority > priority ? 1 : -1;
    83                 if(!over && s.over)
    84                         return -1;
    85                 // we have no idea how to order other objects :-)
    86                 return 0;
    87         }
     79    public int compareTo(LineElemStyle s)
     80    {
     81        if(s.priority != priority)
     82            return s.priority > priority ? 1 : -1;
     83        if(!over && s.over)
     84            return -1;
     85        // we have no idea how to order other objects :-)
     86        return 0;
     87    }
    8888}
  • trunk/src/org/openstreetmap/josm/gui/mappaint/MapPaintStyles.java

    r999 r1169  
    1616public class MapPaintStyles {
    1717
    18         private static ElemStyles styles = new ElemStyles();
    19         private static String iconDirs;
    20        
    21         public static ElemStyles getStyles()
    22         {
    23                 return styles;
    24         }
     18    private static ElemStyles styles = new ElemStyles();
     19    private static String iconDirs;
    2520
    26         public static ImageIcon getIcon(String name, String styleName)
    27         {
    28                 List<String> dirs = new LinkedList<String>();
    29                 for(String fileset : iconDirs.split(";"))
    30                 {
    31                         String[] a;
    32                         if(fileset.indexOf("=") >= 0)
    33                                 a = fileset.split("=", 2);
    34                         else
    35                                 a = new String[] {"", fileset};
     21    public static ElemStyles getStyles()
     22    {
     23        return styles;
     24    }
    3625
    37                         /* non-prefixed path is generic path, always take it */
    38                         if(a[0].length() == 0 || styleName.equals(a[0]))
    39                                 dirs.add(a[1]);
    40                 }
    41                 ImageIcon i = ImageProvider.getIfAvailable(dirs, "mappaint."+styleName, null, name);           
    42                 if(i == null)
    43                 {
    44                         System.out.println("Mappaint-Style \""+styleName+"\" icon \"" + name + "\" not found.");
    45                         i = ImageProvider.getIfAvailable(dirs, "mappaint."+styleName, null, "misc/no_icon.png");
    46                 }
    47                 return i;
    48         }
     26    public static ImageIcon getIcon(String name, String styleName)
     27    {
     28        List<String> dirs = new LinkedList<String>();
     29        for(String fileset : iconDirs.split(";"))
     30        {
     31            String[] a;
     32            if(fileset.indexOf("=") >= 0)
     33                a = fileset.split("=", 2);
     34            else
     35                a = new String[] {"", fileset};
    4936
    50         public static void readFromPreferences() {
    51                 /* don't prefix icon path, as it should be generic */
    52                 String internalicon = "resource://images/styles/standard/;resource://images/styles/";
    53                 String internalfile = "standard=resource://styles/standard/elemstyles.xml";
     37            /* non-prefixed path is generic path, always take it */
     38            if(a[0].length() == 0 || styleName.equals(a[0]))
     39                dirs.add(a[1]);
     40        }
     41        ImageIcon i = ImageProvider.getIfAvailable(dirs, "mappaint."+styleName, null, name);
     42        if(i == null)
     43        {
     44            System.out.println("Mappaint-Style \""+styleName+"\" icon \"" + name + "\" not found.");
     45            i = ImageProvider.getIfAvailable(dirs, "mappaint."+styleName, null, "misc/no_icon.png");
     46        }
     47        return i;
     48    }
    5449
    55                 iconDirs = Main.pref.get("mappaint.iconpaths");
    56                 iconDirs = iconDirs == null || iconDirs.length() == 0 ? internalicon : iconDirs + ";" + internalicon;
     50    public static void readFromPreferences() {
     51        /* don't prefix icon path, as it should be generic */
     52        String internalicon = "resource://images/styles/standard/;resource://images/styles/";
     53        String internalfile = "standard=resource://styles/standard/elemstyles.xml";
    5754
    58                 String file = Main.pref.get("mappaint.sources");
    59                 file = file == null || file.length() == 0 ? internalfile : internalfile + ";" + file;
     55        iconDirs = Main.pref.get("mappaint.iconpaths");
     56        iconDirs = iconDirs == null || iconDirs.length() == 0 ? internalicon : iconDirs + ";" + internalicon;
    6057
    61                 for(String fileset : file.split(";"))
    62                 {
    63                         try
    64                         {
    65                                 String[] a;
    66                                 if(fileset.indexOf("=") >= 0)
    67                                         a = fileset.split("=", 2);
    68                                 else
    69                                         a = new String[] {"standard", fileset};
    70                                 XMLReader xmlReader = XMLReaderFactory.createXMLReader();
    71                                 ElemStyleHandler handler = new ElemStyleHandler(a[0]);
    72                                 xmlReader.setContentHandler(handler);
    73                                 xmlReader.setErrorHandler(handler);
    74                                 xmlReader.parse(new InputSource(new MirroredInputStream(a[1])));
    75                         }
    76                         catch (Exception e)
    77                         {
    78                                 System.out.println("Mappaint-Style problems: \"" + fileset + "\"");
    79                         }
    80                 }
    81                 iconDirs = null;
    82         }
     58        String file = Main.pref.get("mappaint.sources");
     59        file = file == null || file.length() == 0 ? internalfile : internalfile + ";" + file;
     60
     61        for(String fileset : file.split(";"))
     62        {
     63            try
     64            {
     65                String[] a;
     66                if(fileset.indexOf("=") >= 0)
     67                    a = fileset.split("=", 2);
     68                else
     69                    a = new String[] {"standard", fileset};
     70                XMLReader xmlReader = XMLReaderFactory.createXMLReader();
     71                ElemStyleHandler handler = new ElemStyleHandler(a[0]);
     72                xmlReader.setContentHandler(handler);
     73                xmlReader.setErrorHandler(handler);
     74                xmlReader.parse(new InputSource(new MirroredInputStream(a[1])));
     75            }
     76            catch (Exception e)
     77            {
     78                System.out.println("Mappaint-Style problems: \"" + fileset + "\"");
     79            }
     80        }
     81        iconDirs = null;
     82    }
    8383
    8484}
  • trunk/src/org/openstreetmap/josm/gui/preferences/AdvancedPreference.java

    r1053 r1169  
    3131public class AdvancedPreference implements PreferenceSetting {
    3232
    33         private Map<String,String> orig;
    34         private Map<String,String> defaults;
    35         private DefaultTableModel model;
     33    private Map<String,String> orig;
     34    private Map<String,String> defaults;
     35    private DefaultTableModel model;
    3636
    37         public void addGui(final PreferenceDialog gui) {
    38                 JPanel p = gui.createPreferenceTab("advanced", tr("Advanced Preferences"),
    39                         tr("Setting Preference entries directly. Use with caution!"), false);
     37    public void addGui(final PreferenceDialog gui) {
     38        JPanel p = gui.createPreferenceTab("advanced", tr("Advanced Preferences"),
     39                tr("Setting Preference entries directly. Use with caution!"), false);
    4040
    41                 model = new DefaultTableModel(new String[]{tr("Key"), tr("Value")},0) {
    42                         @Override public boolean isCellEditable(int row, int column) {
    43                                 return column != 0;
    44                         }
    45                 };
    46                 DefaultTableCellRenderer renderer = new DefaultTableCellRenderer(){
    47                         public Component getTableCellRendererComponent(JTable table, Object value,
    48                                 boolean isSelected, boolean hasFocus, int row, int column)
    49                         {
    50                                 JLabel label=new JLabel();
    51                                 String s = defaults.get(value);
    52                                 if(s != null)
    53                                 {
    54                                         if(s.equals(model.getValueAt(row, 1)))
    55                                                 label.setToolTipText(tr("Current value is default."));
    56                                         else
    57                                                 label.setToolTipText(tr("Default value is ''{0}''.", s));
    58                                 }
    59                                 else
    60                                         label.setToolTipText(tr("Default value currently unknown (setting has not been used yet)."));
    61                                 label.setText((String)value);
    62                                 return label;
    63                         }
    64                 };
    65                 final JTable list = new JTable(model);
    66                 list.getColumn(tr("Key")).setCellRenderer(renderer);
    67                 JScrollPane scroll = new JScrollPane(list);
    68                 p.add(scroll, GBC.eol().fill(GBC.BOTH));
    69                 scroll.setPreferredSize(new Dimension(400,200));
     41        model = new DefaultTableModel(new String[]{tr("Key"), tr("Value")},0) {
     42            @Override public boolean isCellEditable(int row, int column) {
     43                return column != 0;
     44            }
     45        };
     46        DefaultTableCellRenderer renderer = new DefaultTableCellRenderer(){
     47            public Component getTableCellRendererComponent(JTable table, Object value,
     48                boolean isSelected, boolean hasFocus, int row, int column)
     49            {
     50                JLabel label=new JLabel();
     51                String s = defaults.get(value);
     52                if(s != null)
     53                {
     54                    if(s.equals(model.getValueAt(row, 1)))
     55                        label.setToolTipText(tr("Current value is default."));
     56                    else
     57                        label.setToolTipText(tr("Default value is ''{0}''.", s));
     58                }
     59                else
     60                    label.setToolTipText(tr("Default value currently unknown (setting has not been used yet)."));
     61                label.setText((String)value);
     62                return label;
     63            }
     64        };
     65        final JTable list = new JTable(model);
     66        list.getColumn(tr("Key")).setCellRenderer(renderer);
     67        JScrollPane scroll = new JScrollPane(list);
     68        p.add(scroll, GBC.eol().fill(GBC.BOTH));
     69        scroll.setPreferredSize(new Dimension(400,200));
    7070
    71                 orig = Main.pref.getAllPrefix("");
    72                 defaults = Main.pref.getDefaults();
    73                 orig.remove("osm-server.password");
    74                 defaults.remove("osm-server.password");
    75                 TreeSet<String> ts = new TreeSet<String>(orig.keySet());
    76                 for (String s : defaults.keySet())
    77                 {
    78                         if(!ts.contains(s))
    79                                 ts.add(s);
    80                 }
     71        orig = Main.pref.getAllPrefix("");
     72        defaults = Main.pref.getDefaults();
     73        orig.remove("osm-server.password");
     74        defaults.remove("osm-server.password");
     75        TreeSet<String> ts = new TreeSet<String>(orig.keySet());
     76        for (String s : defaults.keySet())
     77        {
     78            if(!ts.contains(s))
     79                ts.add(s);
     80        }
    8181
    82                 for (String s : ts)
    83                 {
    84                         String val = Main.pref.get(s);
    85                         if(val == null) val = "";
    86                         model.addRow(new String[]{s, val});
    87                 }
     82        for (String s : ts)
     83        {
     84            String val = Main.pref.get(s);
     85            if(val == null) val = "";
     86            model.addRow(new String[]{s, val});
     87        }
    8888
    89                 JButton add = new JButton(tr("Add"));
    90                 p.add(Box.createHorizontalGlue(), GBC.std().fill(GBC.HORIZONTAL));
    91                 p.add(add, GBC.std().insets(0,5,0,0));
    92                 add.addActionListener(new ActionListener(){
    93                         public void actionPerformed(ActionEvent e) {
    94                                 JPanel p = new JPanel(new GridBagLayout());
    95                                 p.add(new JLabel(tr("Key")), GBC.std().insets(0,0,5,0));
    96                                 JTextField key = new JTextField(10);
    97                                 JTextField value = new JTextField(10);
    98                                 p.add(key, GBC.eop().insets(5,0,0,0).fill(GBC.HORIZONTAL));
    99                                 p.add(new JLabel(tr("Value")), GBC.std().insets(0,0,5,0));
    100                                 p.add(value, GBC.eol().insets(5,0,0,0).fill(GBC.HORIZONTAL));
    101                                 int answer = JOptionPane.showConfirmDialog(gui, p, tr("Enter a new key/value pair"), JOptionPane.OK_CANCEL_OPTION);
    102                                 if (answer == JOptionPane.OK_OPTION)
    103                                         model.addRow(new String[]{key.getText(), value.getText()});
    104                         }
    105                 });
     89        JButton add = new JButton(tr("Add"));
     90        p.add(Box.createHorizontalGlue(), GBC.std().fill(GBC.HORIZONTAL));
     91        p.add(add, GBC.std().insets(0,5,0,0));
     92        add.addActionListener(new ActionListener(){
     93            public void actionPerformed(ActionEvent e) {
     94                JPanel p = new JPanel(new GridBagLayout());
     95                p.add(new JLabel(tr("Key")), GBC.std().insets(0,0,5,0));
     96                JTextField key = new JTextField(10);
     97                JTextField value = new JTextField(10);
     98                p.add(key, GBC.eop().insets(5,0,0,0).fill(GBC.HORIZONTAL));
     99                p.add(new JLabel(tr("Value")), GBC.std().insets(0,0,5,0));
     100                p.add(value, GBC.eol().insets(5,0,0,0).fill(GBC.HORIZONTAL));
     101                int answer = JOptionPane.showConfirmDialog(gui, p, tr("Enter a new key/value pair"), JOptionPane.OK_CANCEL_OPTION);
     102                if (answer == JOptionPane.OK_OPTION)
     103                    model.addRow(new String[]{key.getText(), value.getText()});
     104            }
     105        });
    106106
    107                 JButton edit = new JButton(tr("Edit"));
    108                 p.add(edit, GBC.std().insets(5,5,5,0));
    109                 edit.addActionListener(new ActionListener(){
    110                         public void actionPerformed(ActionEvent e) {
    111                                 edit(gui, list);
    112                         }
    113                 });
     107        JButton edit = new JButton(tr("Edit"));
     108        p.add(edit, GBC.std().insets(5,5,5,0));
     109        edit.addActionListener(new ActionListener(){
     110            public void actionPerformed(ActionEvent e) {
     111                edit(gui, list);
     112            }
     113        });
    114114
    115                 JButton delete = new JButton(tr("Delete"));
    116                 p.add(delete, GBC.std().insets(0,5,0,0));
    117                 delete.addActionListener(new ActionListener(){
    118                         public void actionPerformed(ActionEvent e) {
    119                                 if (list.getSelectedRowCount() == 0) {
    120                                         JOptionPane.showMessageDialog(gui, tr("Please select the row to delete."));
    121                                         return;
    122                                 }
    123                                 for(int row: list.getSelectedRows())
    124                                         model.setValueAt("", row, 1);
    125                         }
    126                 });
     115        JButton delete = new JButton(tr("Delete"));
     116        p.add(delete, GBC.std().insets(0,5,0,0));
     117        delete.addActionListener(new ActionListener(){
     118            public void actionPerformed(ActionEvent e) {
     119                if (list.getSelectedRowCount() == 0) {
     120                    JOptionPane.showMessageDialog(gui, tr("Please select the row to delete."));
     121                    return;
     122                }
     123                for(int row: list.getSelectedRows())
     124                    model.setValueAt("", row, 1);
     125            }
     126        });
    127127
    128                 list.addMouseListener(new MouseAdapter(){
    129                         @Override public void mouseClicked(MouseEvent e) {
    130                                 if (e.getClickCount() == 2)
    131                                         edit(gui, list);
    132                         }
    133                 });
    134         }
     128        list.addMouseListener(new MouseAdapter(){
     129            @Override public void mouseClicked(MouseEvent e) {
     130                if (e.getClickCount() == 2)
     131                    edit(gui, list);
     132            }
     133        });
     134    }
    135135
    136         public void ok() {
    137                 for (int i = 0; i < model.getRowCount(); ++i) {
    138                         String value = model.getValueAt(i,1).toString();
    139                         if(value.length() != 0)
    140                         {
    141                                 String key = model.getValueAt(i,0).toString();
    142                                 String origValue = orig.get(key);
    143                                 if (origValue == null || !origValue.equals(value))
    144                                         Main.pref.put(key, value);
    145                                 orig.remove(key); // processed.
    146                         }
    147                 }
    148                 for (Entry<String, String> e : orig.entrySet())
    149                         Main.pref.put(e.getKey(), null);
    150         }
     136    public void ok() {
     137        for (int i = 0; i < model.getRowCount(); ++i) {
     138            String value = model.getValueAt(i,1).toString();
     139            if(value.length() != 0)
     140            {
     141                String key = model.getValueAt(i,0).toString();
     142                String origValue = orig.get(key);
     143                if (origValue == null || !origValue.equals(value))
     144                    Main.pref.put(key, value);
     145                orig.remove(key); // processed.
     146            }
     147        }
     148        for (Entry<String, String> e : orig.entrySet())
     149            Main.pref.put(e.getKey(), null);
     150    }
    151151
    152152
    153         private void edit(final PreferenceDialog gui, final JTable list) {
    154                 if (list.getSelectedRowCount() != 1) {
    155                         JOptionPane.showMessageDialog(gui, tr("Please select the row to edit."));
    156                         return;
    157                 }
    158                 String v = JOptionPane.showInputDialog(tr("New value for {0}", model.getValueAt(list.getSelectedRow(), 0)), model.getValueAt(list.getSelectedRow(), 1));
    159                 if (v != null)
    160                         model.setValueAt(v, list.getSelectedRow(), 1);
    161         }
     153    private void edit(final PreferenceDialog gui, final JTable list) {
     154        if (list.getSelectedRowCount() != 1) {
     155            JOptionPane.showMessageDialog(gui, tr("Please select the row to edit."));
     156            return;
     157        }
     158        String v = JOptionPane.showInputDialog(tr("New value for {0}", model.getValueAt(list.getSelectedRow(), 0)), model.getValueAt(list.getSelectedRow(), 1));
     159        if (v != null)
     160            model.setValueAt(v, list.getSelectedRow(), 1);
     161    }
    162162}
  • trunk/src/org/openstreetmap/josm/gui/preferences/AudioPreference.java

    r813 r1169  
    2727
    2828public class AudioPreference implements PreferenceSetting {
    29         private JCheckBox audioMenuVisible = new JCheckBox(tr("Display the Audio menu."));
    30         private JCheckBox markerButtonLabels = new JCheckBox(tr("Label audio (and image and web) markers."));
    31         private JCheckBox markerAudioTraceVisible = new JCheckBox(tr("Display live audio trace."));
    32         private JCheckBox makeAutoMarkers = new JCheckBox(tr("Create non-audio markers when reading GPX."));
     29    private JCheckBox audioMenuVisible = new JCheckBox(tr("Display the Audio menu."));
     30    private JCheckBox markerButtonLabels = new JCheckBox(tr("Label audio (and image and web) markers."));
     31    private JCheckBox markerAudioTraceVisible = new JCheckBox(tr("Display live audio trace."));
     32    private JCheckBox makeAutoMarkers = new JCheckBox(tr("Create non-audio markers when reading GPX."));
    3333
    34         // various methods of making markers on import audio
    35         private JCheckBox audioMarkersFromExplicitWaypoints = new JCheckBox(tr("Explicit waypoints with valid timestamps."));
    36         private JCheckBox audioMarkersFromUntimedWaypoints = new JCheckBox(tr("Explicit waypoints with time estimated from track position."));
    37         private JCheckBox audioMarkersFromNamedTrackpoints = new JCheckBox(tr("Named trackpoints."));
    38         private JCheckBox audioMarkersFromStart = new JCheckBox(tr("Start of track (will always do this if no other markers available)."));
     34    // various methods of making markers on import audio
     35    private JCheckBox audioMarkersFromExplicitWaypoints = new JCheckBox(tr("Explicit waypoints with valid timestamps."));
     36    private JCheckBox audioMarkersFromUntimedWaypoints = new JCheckBox(tr("Explicit waypoints with time estimated from track position."));
     37    private JCheckBox audioMarkersFromNamedTrackpoints = new JCheckBox(tr("Named trackpoints."));
     38    private JCheckBox audioMarkersFromStart = new JCheckBox(tr("Start of track (will always do this if no other markers available)."));
    3939
    40         private JTextField audioLeadIn = new JTextField(8);
    41         private JTextField audioForwardBackAmount = new JTextField(8);
    42         private JTextField audioFastForwardMultiplier = new JTextField(8);
    43         private JTextField audioCalibration = new JTextField(8);
     40    private JTextField audioLeadIn = new JTextField(8);
     41    private JTextField audioForwardBackAmount = new JTextField(8);
     42    private JTextField audioFastForwardMultiplier = new JTextField(8);
     43    private JTextField audioCalibration = new JTextField(8);
    4444
    45         public void addGui(PreferenceDialog gui) {
    46                 // audioMenuVisible
    47                 audioMenuVisible.addActionListener(new ActionListener(){
    48                         public void actionPerformed(ActionEvent e) {
    49                                 if (!audioMenuVisible.isSelected())
    50                                         audioMenuVisible.setSelected(false);
    51                                 audioMenuVisible.setEnabled(audioMenuVisible.isSelected());
    52                         }
    53                 });
    54                 audioMenuVisible.setSelected(! Main.pref.getBoolean("audio.menuinvisible"));
    55                 audioMenuVisible.setToolTipText(tr("Show or hide the audio menu entry on the main menu bar."));
    56                 gui.audio.add(audioMenuVisible, GBC.eol().insets(0,0,0,0));
     45    public void addGui(PreferenceDialog gui) {
     46        // audioMenuVisible
     47        audioMenuVisible.addActionListener(new ActionListener(){
     48            public void actionPerformed(ActionEvent e) {
     49                if (!audioMenuVisible.isSelected())
     50                    audioMenuVisible.setSelected(false);
     51                audioMenuVisible.setEnabled(audioMenuVisible.isSelected());
     52            }
     53        });
     54        audioMenuVisible.setSelected(! Main.pref.getBoolean("audio.menuinvisible"));
     55        audioMenuVisible.setToolTipText(tr("Show or hide the audio menu entry on the main menu bar."));
     56        gui.audio.add(audioMenuVisible, GBC.eol().insets(0,0,0,0));
    5757
    58                 // audioTraceVisible
    59                 markerAudioTraceVisible.addActionListener(new ActionListener(){
    60                         public void actionPerformed(ActionEvent e) {
    61                                 if (!markerAudioTraceVisible.isSelected())
    62                                         markerAudioTraceVisible.setSelected(false);
    63                         }
    64                 });
    65                 markerAudioTraceVisible.setSelected(Main.pref.getBoolean("marker.traceaudio", true));
    66                 markerAudioTraceVisible.setToolTipText(tr("Display a moving icon representing the point on the synchronized track where the audio currently playing was recorded."));
    67                 gui.audio.add(markerAudioTraceVisible, GBC.eol().insets(0,0,0,0));
    68                
    69                 // buttonLabels
    70                 markerButtonLabels.addActionListener(new ActionListener(){
    71                         public void actionPerformed(ActionEvent e) {
    72                                 if (!markerButtonLabels.isSelected())
    73                                         markerButtonLabels.setSelected(false);
    74                         }
    75                 });
    76                 markerButtonLabels.setSelected(Main.pref.getBoolean("marker.buttonlabels"));
    77                 markerButtonLabels.setToolTipText(tr("Put text labels against audio (and image and web) markers as well as their button icons."));
    78                 gui.audio.add(markerButtonLabels, GBC.eol().insets(0,0,0,0));
    79                
    80                 // makeAutoMarkers
    81                 makeAutoMarkers.addActionListener(new ActionListener(){
    82                         public void actionPerformed(ActionEvent e) {
    83                                 if (!makeAutoMarkers.isSelected())
    84                                         makeAutoMarkers.setSelected(false);
    85                         }
    86                 });
    87                 makeAutoMarkers.setSelected(Main.pref.getBoolean("marker.makeautomarkers", true));
    88                 makeAutoMarkers.setToolTipText(tr("Automatically make a marker layer from any waypoints when opening a GPX layer."));
    89                 gui.audio.add(makeAutoMarkers, GBC.eol().insets(0,0,0,0));
    90                
    91                 gui.audio.add(new JLabel(tr("When importing audio, make markers from...")), GBC.eol());
     58        // audioTraceVisible
     59        markerAudioTraceVisible.addActionListener(new ActionListener(){
     60            public void actionPerformed(ActionEvent e) {
     61                if (!markerAudioTraceVisible.isSelected())
     62                    markerAudioTraceVisible.setSelected(false);
     63            }
     64        });
     65        markerAudioTraceVisible.setSelected(Main.pref.getBoolean("marker.traceaudio", true));
     66        markerAudioTraceVisible.setToolTipText(tr("Display a moving icon representing the point on the synchronized track where the audio currently playing was recorded."));
     67        gui.audio.add(markerAudioTraceVisible, GBC.eol().insets(0,0,0,0));
    9268
    93                 // audioMarkersFromExplicitWaypoints
    94                 audioMarkersFromExplicitWaypoints.addActionListener(new ActionListener(){
    95                         public void actionPerformed(ActionEvent e) {
    96                                 if (!audioMarkersFromExplicitWaypoints.isSelected())
    97                                         audioMarkersFromExplicitWaypoints.setSelected(false);
    98                         }
    99                 });
    100                 audioMarkersFromExplicitWaypoints.setSelected(Main.pref.getBoolean("marker.audiofromexplicitwaypoints", true));
    101                 audioMarkersFromExplicitWaypoints.setToolTipText(tr("When importing audio, apply it to any waypoints in the GPX layer."));
    102                 gui.audio.add(audioMarkersFromExplicitWaypoints, GBC.eol().insets(10,0,0,0));
    103                
    104                 // audioMarkersFromUntimedWaypoints
    105                 audioMarkersFromUntimedWaypoints.addActionListener(new ActionListener(){
    106                         public void actionPerformed(ActionEvent e) {
    107                                 if (!audioMarkersFromUntimedWaypoints.isSelected())
    108                                         audioMarkersFromUntimedWaypoints.setSelected(false);
    109                         }
    110                 });
    111                 audioMarkersFromUntimedWaypoints.setSelected(Main.pref.getBoolean("marker.audiofromuntimedwaypoints", true));
    112                 audioMarkersFromUntimedWaypoints.setToolTipText(tr("When importing audio, apply it to any waypoints in the GPX layer."));
    113                 gui.audio.add(audioMarkersFromUntimedWaypoints, GBC.eol().insets(10,0,0,0));
    114                
    115                 // audioMarkersFromNamedTrackpoints
    116                 audioMarkersFromNamedTrackpoints.addActionListener(new ActionListener(){
    117                         public void actionPerformed(ActionEvent e) {
    118                                 if (!audioMarkersFromNamedTrackpoints.isSelected())
    119                                         audioMarkersFromNamedTrackpoints.setSelected(false);
    120                         }
    121                 });
    122                 audioMarkersFromNamedTrackpoints.setSelected(Main.pref.getBoolean("marker.audiofromnamedtrackpoints", false));
    123                 audioMarkersFromNamedTrackpoints.setToolTipText(tr("Automatically create audio markers from trackpoints (rather than explicit waypoints) with names or descriptions."));
    124                 gui.audio.add(audioMarkersFromNamedTrackpoints, GBC.eol().insets(10,0,0,0));
    125                
    126                 // audioMarkersFromStart
    127                 audioMarkersFromStart.addActionListener(new ActionListener(){
    128                         public void actionPerformed(ActionEvent e) {
    129                                 if (!audioMarkersFromStart.isSelected())
    130                                         audioMarkersFromStart.setSelected(false);
    131                         }
    132                 });
    133                 audioMarkersFromStart.setSelected(Main.pref.getBoolean("marker.audiofromstart"));
    134                 audioMarkersFromStart.setToolTipText(tr("Automatically create audio markers from trackpoints (rather than explicit waypoints) with names or descriptions."));
    135                 gui.audio.add(audioMarkersFromStart, GBC.eol().insets(10,0,0,0));
    136                
    137                 audioForwardBackAmount.setText(Main.pref.get("audio.forwardbackamount", "10.0"));
    138                 audioForwardBackAmount.setToolTipText(tr("The number of seconds to jump forward or back when the relevant button is pressed"));
    139                 gui.audio.add(new JLabel(tr("Forward/back time (seconds)")), GBC.std());
    140                 gui.audio.add(audioForwardBackAmount, GBC.eol().fill(GBC.HORIZONTAL).insets(5,0,0,5));
     69        // buttonLabels
     70        markerButtonLabels.addActionListener(new ActionListener(){
     71            public void actionPerformed(ActionEvent e) {
     72                if (!markerButtonLabels.isSelected())
     73                    markerButtonLabels.setSelected(false);
     74            }
     75        });
     76        markerButtonLabels.setSelected(Main.pref.getBoolean("marker.buttonlabels"));
     77        markerButtonLabels.setToolTipText(tr("Put text labels against audio (and image and web) markers as well as their button icons."));
     78        gui.audio.add(markerButtonLabels, GBC.eol().insets(0,0,0,0));
    14179
    142                 audioFastForwardMultiplier.setText(Main.pref.get("audio.fastfwdmultiplier", "1.3"));
    143                 audioFastForwardMultiplier.setToolTipText(tr("The amount by which the speed is multiplied for fast forwarding"));
    144                 gui.audio.add(new JLabel(tr("Fast forward multiplier")), GBC.std());
    145                 gui.audio.add(audioFastForwardMultiplier, GBC.eol().fill(GBC.HORIZONTAL).insets(5,0,0,5));
     80        // makeAutoMarkers
     81        makeAutoMarkers.addActionListener(new ActionListener(){
     82            public void actionPerformed(ActionEvent e) {
     83                if (!makeAutoMarkers.isSelected())
     84                    makeAutoMarkers.setSelected(false);
     85            }
     86        });
     87        makeAutoMarkers.setSelected(Main.pref.getBoolean("marker.makeautomarkers", true));
     88        makeAutoMarkers.setToolTipText(tr("Automatically make a marker layer from any waypoints when opening a GPX layer."));
     89        gui.audio.add(makeAutoMarkers, GBC.eol().insets(0,0,0,0));
    14690
    147                 audioLeadIn.setText(Main.pref.get("audio.leadin", "1"));
    148                 audioLeadIn.setToolTipText(tr("Playback starts this number of seconds before (or after, if negative) the audio track position requested"));
    149                 gui.audio.add(new JLabel(tr("Lead-in time (seconds)")), GBC.std());
    150                 gui.audio.add(audioLeadIn, GBC.eol().fill(GBC.HORIZONTAL).insets(5,0,0,5));
     91        gui.audio.add(new JLabel(tr("When importing audio, make markers from...")), GBC.eol());
    15192
    152                 audioCalibration.setText(Main.pref.get("audio.calibration", "1.0"));
    153                 audioCalibration.setToolTipText(tr("The ratio of voice recorder elapsed time to true elapsed time"));
    154                 gui.audio.add(new JLabel(tr("Voice recorder calibration")), GBC.std());
    155                 gui.audio.add(audioCalibration, GBC.eol().fill(GBC.HORIZONTAL).insets(5,0,0,5));
     93        // audioMarkersFromExplicitWaypoints
     94        audioMarkersFromExplicitWaypoints.addActionListener(new ActionListener(){
     95            public void actionPerformed(ActionEvent e) {
     96                if (!audioMarkersFromExplicitWaypoints.isSelected())
     97                    audioMarkersFromExplicitWaypoints.setSelected(false);
     98            }
     99        });
     100        audioMarkersFromExplicitWaypoints.setSelected(Main.pref.getBoolean("marker.audiofromexplicitwaypoints", true));
     101        audioMarkersFromExplicitWaypoints.setToolTipText(tr("When importing audio, apply it to any waypoints in the GPX layer."));
     102        gui.audio.add(audioMarkersFromExplicitWaypoints, GBC.eol().insets(10,0,0,0));
    156103
    157                 gui.audio.add(Box.createVerticalGlue(), GBC.eol().fill(GBC.VERTICAL));
    158         }
     104        // audioMarkersFromUntimedWaypoints
     105        audioMarkersFromUntimedWaypoints.addActionListener(new ActionListener(){
     106            public void actionPerformed(ActionEvent e) {
     107                if (!audioMarkersFromUntimedWaypoints.isSelected())
     108                    audioMarkersFromUntimedWaypoints.setSelected(false);
     109            }
     110        });
     111        audioMarkersFromUntimedWaypoints.setSelected(Main.pref.getBoolean("marker.audiofromuntimedwaypoints", true));
     112        audioMarkersFromUntimedWaypoints.setToolTipText(tr("When importing audio, apply it to any waypoints in the GPX layer."));
     113        gui.audio.add(audioMarkersFromUntimedWaypoints, GBC.eol().insets(10,0,0,0));
    159114
    160         public void ok() {
    161                 Main.pref.put("audio.menuinvisible", ! audioMenuVisible.isSelected());
    162                 Main.pref.put("marker.traceaudio", markerAudioTraceVisible.isSelected());
    163                 Main.pref.put("marker.buttonlabels", markerButtonLabels.isSelected());
    164                 Main.pref.put("marker.makeautomarkers", makeAutoMarkers.isSelected());
    165                 Main.pref.put("marker.audiofromexplicitwaypoints", audioMarkersFromExplicitWaypoints.isSelected());
    166                 Main.pref.put("marker.audiofromuntimedwaypoints", audioMarkersFromUntimedWaypoints.isSelected());
    167                 Main.pref.put("marker.audiofromnamedtrackpoints", audioMarkersFromNamedTrackpoints.isSelected());
    168                 Main.pref.put("marker.audiofromstart", audioMarkersFromStart.isSelected());
    169                 Main.pref.put("audio.forwardbackamount", audioForwardBackAmount.getText());             
    170                 Main.pref.put("audio.fastfwdmultiplier", audioFastForwardMultiplier.getText());         
    171                 Main.pref.put("audio.leadin", audioLeadIn.getText());           
    172                 Main.pref.put("audio.calibration", audioCalibration.getText());         
     115        // audioMarkersFromNamedTrackpoints
     116        audioMarkersFromNamedTrackpoints.addActionListener(new ActionListener(){
     117            public void actionPerformed(ActionEvent e) {
     118                if (!audioMarkersFromNamedTrackpoints.isSelected())
     119                    audioMarkersFromNamedTrackpoints.setSelected(false);
     120            }
     121        });
     122        audioMarkersFromNamedTrackpoints.setSelected(Main.pref.getBoolean("marker.audiofromnamedtrackpoints", false));
     123        audioMarkersFromNamedTrackpoints.setToolTipText(tr("Automatically create audio markers from trackpoints (rather than explicit waypoints) with names or descriptions."));
     124        gui.audio.add(audioMarkersFromNamedTrackpoints, GBC.eol().insets(10,0,0,0));
     125
     126        // audioMarkersFromStart
     127        audioMarkersFromStart.addActionListener(new ActionListener(){
     128            public void actionPerformed(ActionEvent e) {
     129                if (!audioMarkersFromStart.isSelected())
     130                    audioMarkersFromStart.setSelected(false);
     131            }
     132        });
     133        audioMarkersFromStart.setSelected(Main.pref.getBoolean("marker.audiofromstart"));
     134        audioMarkersFromStart.setToolTipText(tr("Automatically create audio markers from trackpoints (rather than explicit waypoints) with names or descriptions."));
     135        gui.audio.add(audioMarkersFromStart, GBC.eol().insets(10,0,0,0));
     136
     137        audioForwardBackAmount.setText(Main.pref.get("audio.forwardbackamount", "10.0"));
     138        audioForwardBackAmount.setToolTipText(tr("The number of seconds to jump forward or back when the relevant button is pressed"));
     139        gui.audio.add(new JLabel(tr("Forward/back time (seconds)")), GBC.std());
     140        gui.audio.add(audioForwardBackAmount, GBC.eol().fill(GBC.HORIZONTAL).insets(5,0,0,5));
     141
     142        audioFastForwardMultiplier.setText(Main.pref.get("audio.fastfwdmultiplier", "1.3"));
     143        audioFastForwardMultiplier.setToolTipText(tr("The amount by which the speed is multiplied for fast forwarding"));
     144        gui.audio.add(new JLabel(tr("Fast forward multiplier")), GBC.std());
     145        gui.audio.add(audioFastForwardMultiplier, GBC.eol().fill(GBC.HORIZONTAL).insets(5,0,0,5));
     146
     147        audioLeadIn.setText(Main.pref.get("audio.leadin", "1"));
     148        audioLeadIn.setToolTipText(tr("Playback starts this number of seconds before (or after, if negative) the audio track position requested"));
     149        gui.audio.add(new JLabel(tr("Lead-in time (seconds)")), GBC.std());
     150        gui.audio.add(audioLeadIn, GBC.eol().fill(GBC.HORIZONTAL).insets(5,0,0,5));
     151
     152        audioCalibration.setText(Main.pref.get("audio.calibration", "1.0"));
     153        audioCalibration.setToolTipText(tr("The ratio of voice recorder elapsed time to true elapsed time"));
     154        gui.audio.add(new JLabel(tr("Voice recorder calibration")), GBC.std());
     155        gui.audio.add(audioCalibration, GBC.eol().fill(GBC.HORIZONTAL).insets(5,0,0,5));
     156
     157        gui.audio.add(Box.createVerticalGlue(), GBC.eol().fill(GBC.VERTICAL));
     158    }
     159
     160    public void ok() {
     161        Main.pref.put("audio.menuinvisible", ! audioMenuVisible.isSelected());
     162        Main.pref.put("marker.traceaudio", markerAudioTraceVisible.isSelected());
     163        Main.pref.put("marker.buttonlabels", markerButtonLabels.isSelected());
     164        Main.pref.put("marker.makeautomarkers", makeAutoMarkers.isSelected());
     165        Main.pref.put("marker.audiofromexplicitwaypoints", audioMarkersFromExplicitWaypoints.isSelected());
     166        Main.pref.put("marker.audiofromuntimedwaypoints", audioMarkersFromUntimedWaypoints.isSelected());
     167        Main.pref.put("marker.audiofromnamedtrackpoints", audioMarkersFromNamedTrackpoints.isSelected());
     168        Main.pref.put("marker.audiofromstart", audioMarkersFromStart.isSelected());
     169        Main.pref.put("audio.forwardbackamount", audioForwardBackAmount.getText());
     170        Main.pref.put("audio.fastfwdmultiplier", audioFastForwardMultiplier.getText());
     171        Main.pref.put("audio.leadin", audioLeadIn.getText());
     172        Main.pref.put("audio.calibration", audioCalibration.getText());
    173173    }
    174174}
  • trunk/src/org/openstreetmap/josm/gui/preferences/ColorPreference.java

    r1165 r1169  
    4040public class ColorPreference implements PreferenceSetting {
    4141
    42         private DefaultTableModel tableModel;
    43         private JTable colors;
     42    private DefaultTableModel tableModel;
     43    private JTable colors;
    4444
    45         /**
    46          * Set the colors to be shown in the preference table. This method creates a table model if
    47          * none exists and overwrites all existing values.
    48          * @param colorMap the map holding the colors
    49          * (key = color id (without prefixes, so only <code>background</code>; not <code>color.background</code>),
    50          * value = html representation of the color.
    51          */
    52         public void setColorModel(Map<String, String> colorMap) {
    53                 if(tableModel == null) {
    54                         tableModel = new DefaultTableModel();
    55                         tableModel.addColumn(tr("Color"));
    56                         tableModel.addColumn(tr("Name"));
    57                 }
    58                
    59                 // clear old model:
    60                 while(tableModel.getRowCount() > 0) {
    61                         tableModel.removeRow(0);
    62                 }
    63                 // fill model with colors:
    64                 List<String> colorKeyList = new ArrayList<String>();
    65                 for(String key : colorMap.keySet()) {
    66                         colorKeyList.add(key);
    67                 }
    68                 Collections.sort(colorKeyList);
    69                 for (String key : colorKeyList) {
    70                         Vector<Object> row = new Vector<Object>(2);
    71                         row.add(key);
    72                         row.add(ColorHelper.html2color(colorMap.get(key)));
    73                         tableModel.addRow(row);
    74                 }
    75                 if(this.colors != null) {
    76                         this.colors.repaint();
    77                 }               
    78         }
    79        
    80         /**
    81          * Returns a map with the colors in the table (key = color name without prefix, value = html color code).
    82          * @return a map holding the colors.
    83          */
    84         public Map<String, String> getColorModel() {
    85                 String key;
    86                 String value;
    87                 Map<String, String> colorMap = new HashMap<String, String>();
    88                 for(int row = 0; row < tableModel.getRowCount(); ++row) {
    89                         key = (String)tableModel.getValueAt(row, 0);
    90                         value = ColorHelper.color2html((Color)tableModel.getValueAt(row, 1));
    91                         colorMap.put(key, value);
    92                 }
    93                 return colorMap;
    94         }
    95        
    96         public void addGui(final PreferenceDialog gui) {
    97                 // initial fill with colors from preferences:
    98                 Map<String,String> prefColorMap = new TreeMap<String, String>(Main.pref.getAllPrefix("color."));
    99                 fixColorPrefixes(prefColorMap);
    100                 Map<String,String> colorMap = new TreeMap<String, String>();
    101                 for(String key : prefColorMap.keySet()) {
    102                         colorMap.put(key.substring("color.".length()), prefColorMap.get(key));
    103                 }
    104                 setColorModel(colorMap);
    105                
    106                 colors = new JTable(tableModel) {
    107                         @Override public boolean isCellEditable(int row, int column) {
    108                                 return false;
    109                         }
    110                 };
    111                 colors.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
    112                 final TableCellRenderer oldColorsRenderer = colors.getDefaultRenderer(Object.class);
    113                 colors.setDefaultRenderer(Object.class, new TableCellRenderer(){
    114                         public Component getTableCellRendererComponent(JTable t, Object o, boolean selected, boolean focus, int row, int column) {
    115                                 if (column == 1) {
    116                                         JLabel l = new JLabel(ColorHelper.color2html((Color)o));
    117                                         l.setBackground((Color)o);
    118                                         l.setOpaque(true);
    119                                         return l;
    120                                 }
    121                                 return oldColorsRenderer.getTableCellRendererComponent(t,tr(o.toString()),selected,focus,row,column);
    122                         }
    123                 });
    124                 colors.getColumnModel().getColumn(1).setWidth(100);
     45    /**
     46     * Set the colors to be shown in the preference table. This method creates a table model if
     47     * none exists and overwrites all existing values.
     48     * @param colorMap the map holding the colors
     49     * (key = color id (without prefixes, so only <code>background</code>; not <code>color.background</code>),
     50     * value = html representation of the color.
     51     */
     52    public void setColorModel(Map<String, String> colorMap) {
     53        if(tableModel == null) {
     54            tableModel = new DefaultTableModel();
     55            tableModel.addColumn(tr("Color"));
     56            tableModel.addColumn(tr("Name"));
     57        }
    12558
    126                 JButton colorEdit = new JButton(tr("Choose"));
    127                 colorEdit.addActionListener(new ActionListener(){
    128                         public void actionPerformed(ActionEvent e) {
    129                                 if (colors.getSelectedRowCount() == 0) {
    130                                         JOptionPane.showMessageDialog(gui, tr("Please select a color."));
    131                                         return;
    132                                 }
    133                                 int sel = colors.getSelectedRow();
    134                                 JColorChooser chooser = new JColorChooser((Color)colors.getValueAt(sel, 1));
    135                                 int answer = JOptionPane.showConfirmDialog(gui, chooser, tr("Choose a color for {0}", colors.getValueAt(sel, 0)), JOptionPane.OK_CANCEL_OPTION);
    136                                 if (answer == JOptionPane.OK_OPTION)
    137                                         colors.setValueAt(chooser.getColor(), sel, 1);
    138                         }
    139                 });
    140                 colors.setToolTipText(tr("Colors used by different objects in JOSM."));
    141                 colors.setPreferredScrollableViewportSize(new Dimension(100,112));
    142 
    143                 JPanel panel = new JPanel(new GridBagLayout());
    144                 panel.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
    145                 JScrollPane scrollpane = new JScrollPane(colors);
    146                 scrollpane.setBorder(BorderFactory.createEmptyBorder( 0, 0, 0, 0 ));
    147                 panel.add(scrollpane, GBC.eol().fill(GBC.BOTH));
    148                 panel.add(colorEdit, GBC.eol().anchor(GBC.EAST));
    149                 gui.displaycontent.addTab(tr("Colors"), panel);
     59        // clear old model:
     60        while(tableModel.getRowCount() > 0) {
     61            tableModel.removeRow(0);
     62        }
     63        // fill model with colors:
     64        List<String> colorKeyList = new ArrayList<String>();
     65        for(String key : colorMap.keySet()) {
     66            colorKeyList.add(key);
     67        }
     68        Collections.sort(colorKeyList);
     69        for (String key : colorKeyList) {
     70            Vector<Object> row = new Vector<Object>(2);
     71            row.add(key);
     72            row.add(ColorHelper.html2color(colorMap.get(key)));
     73            tableModel.addRow(row);
     74        }
     75        if(this.colors != null) {
     76            this.colors.repaint();
     77        }
    15078    }
    15179
    152         /**
    153          * Add all missing color entries.
    154          */
    155         private void fixColorPrefixes(Map<String, String> prefColorMap) {
    156                 String[] cp = {
    157                         marktr("background"), ColorHelper.color2html(Color.black),
    158                         marktr("node"), ColorHelper.color2html(Color.red),
    159                         marktr("way"), ColorHelper.color2html(SimplePaintVisitor.darkblue),
    160                         marktr("incomplete way"), ColorHelper.color2html(SimplePaintVisitor.darkerblue),
    161                         marktr("relation"), ColorHelper.color2html(SimplePaintVisitor.teal),
    162                         marktr("selected"), ColorHelper.color2html(Color.white),
    163                         marktr("gps marker"), ColorHelper.color2html(Color.gray),
    164                         marktr("gps point"), ColorHelper.color2html(Color.gray),
    165                         marktr("conflict"), ColorHelper.color2html(Color.gray),
    166                         marktr("scale"), ColorHelper.color2html(Color.white),
    167                         marktr("inactive"), ColorHelper.color2html(Color.darkGray),
    168                 };
    169                 for (int i = 0; i < cp.length/2; ++i)
    170                 {
    171                         if (!Main.pref.hasKey("color."+cp[i*2]))
    172                                 Main.pref.put("color."+cp[i*2], cp[i*2+1]);
    173                         Main.pref.putDefault("color."+cp[i*2], cp[i*2+1]);
    174                 }
     80    /**
     81     * Returns a map with the colors in the table (key = color name without prefix, value = html color code).
     82     * @return a map holding the colors.
     83     */
     84    public Map<String, String> getColorModel() {
     85        String key;
     86        String value;
     87        Map<String, String> colorMap = new HashMap<String, String>();
     88        for(int row = 0; row < tableModel.getRowCount(); ++row) {
     89            key = (String)tableModel.getValueAt(row, 0);
     90            value = ColorHelper.color2html((Color)tableModel.getValueAt(row, 1));
     91            colorMap.put(key, value);
     92        }
     93        return colorMap;
    17594    }
    17695
    177         public void ok() {
    178                 for (int i = 0; i < colors.getRowCount(); ++i) {
    179                         String name = (String)colors.getValueAt(i, 0);
    180                         Color col = (Color)colors.getValueAt(i, 1);
    181                         Main.pref.put("color." + name, ColorHelper.color2html(col));
    182                 }
     96    public void addGui(final PreferenceDialog gui) {
     97        // initial fill with colors from preferences:
     98        Map<String,String> prefColorMap = new TreeMap<String, String>(Main.pref.getAllPrefix("color."));
     99        fixColorPrefixes(prefColorMap);
     100        Map<String,String> colorMap = new TreeMap<String, String>();
     101        for(String key : prefColorMap.keySet()) {
     102            colorMap.put(key.substring("color.".length()), prefColorMap.get(key));
     103        }
     104        setColorModel(colorMap);
     105
     106        colors = new JTable(tableModel) {
     107            @Override public boolean isCellEditable(int row, int column) {
     108                return false;
     109            }
     110        };
     111        colors.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
     112        final TableCellRenderer oldColorsRenderer = colors.getDefaultRenderer(Object.class);
     113        colors.setDefaultRenderer(Object.class, new TableCellRenderer(){
     114            public Component getTableCellRendererComponent(JTable t, Object o, boolean selected, boolean focus, int row, int column) {
     115                if (column == 1) {
     116                    JLabel l = new JLabel(ColorHelper.color2html((Color)o));
     117                    l.setBackground((Color)o);
     118                    l.setOpaque(true);
     119                    return l;
     120                }
     121                return oldColorsRenderer.getTableCellRendererComponent(t,tr(o.toString()),selected,focus,row,column);
     122            }
     123        });
     124        colors.getColumnModel().getColumn(1).setWidth(100);
     125
     126        JButton colorEdit = new JButton(tr("Choose"));
     127        colorEdit.addActionListener(new ActionListener(){
     128            public void actionPerformed(ActionEvent e) {
     129                if (colors.getSelectedRowCount() == 0) {
     130                    JOptionPane.showMessageDialog(gui, tr("Please select a color."));
     131                    return;
     132                }
     133                int sel = colors.getSelectedRow();
     134                JColorChooser chooser = new JColorChooser((Color)colors.getValueAt(sel, 1));
     135                int answer = JOptionPane.showConfirmDialog(gui, chooser, tr("Choose a color for {0}", colors.getValueAt(sel, 0)), JOptionPane.OK_CANCEL_OPTION);
     136                if (answer == JOptionPane.OK_OPTION)
     137                    colors.setValueAt(chooser.getColor(), sel, 1);
     138            }
     139        });
     140        colors.setToolTipText(tr("Colors used by different objects in JOSM."));
     141        colors.setPreferredScrollableViewportSize(new Dimension(100,112));
     142
     143        JPanel panel = new JPanel(new GridBagLayout());
     144        panel.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
     145        JScrollPane scrollpane = new JScrollPane(colors);
     146        scrollpane.setBorder(BorderFactory.createEmptyBorder( 0, 0, 0, 0 ));
     147        panel.add(scrollpane, GBC.eol().fill(GBC.BOTH));
     148        panel.add(colorEdit, GBC.eol().anchor(GBC.EAST));
     149        gui.displaycontent.addTab(tr("Colors"), panel);
     150    }
     151
     152    /**
     153     * Add all missing color entries.
     154     */
     155    private void fixColorPrefixes(Map<String, String> prefColorMap) {
     156        String[] cp = {
     157            marktr("background"), ColorHelper.color2html(Color.black),
     158            marktr("node"), ColorHelper.color2html(Color.red),
     159            marktr("way"), ColorHelper.color2html(SimplePaintVisitor.darkblue),
     160            marktr("incomplete way"), ColorHelper.color2html(SimplePaintVisitor.darkerblue),
     161            marktr("relation"), ColorHelper.color2html(SimplePaintVisitor.teal),
     162            marktr("selected"), ColorHelper.color2html(Color.white),
     163            marktr("gps marker"), ColorHelper.color2html(Color.gray),
     164            marktr("gps point"), ColorHelper.color2html(Color.gray),
     165            marktr("conflict"), ColorHelper.color2html(Color.gray),
     166            marktr("scale"), ColorHelper.color2html(Color.white),
     167            marktr("inactive"), ColorHelper.color2html(Color.darkGray),
     168        };
     169        for (int i = 0; i < cp.length/2; ++i)
     170        {
     171            if (!Main.pref.hasKey("color."+cp[i*2]))
     172                Main.pref.put("color."+cp[i*2], cp[i*2+1]);
     173            Main.pref.putDefault("color."+cp[i*2], cp[i*2+1]);
     174        }
     175    }
     176
     177    public void ok() {
     178        for (int i = 0; i < colors.getRowCount(); ++i) {
     179            String name = (String)colors.getValueAt(i, 0);
     180            Color col = (Color)colors.getValueAt(i, 1);
     181            Main.pref.put("color." + name, ColorHelper.color2html(col));
     182        }
    183183        org.openstreetmap.josm.gui.layer.OsmDataLayer.createHatchTexture();
    184184    }
  • trunk/src/org/openstreetmap/josm/gui/preferences/DrawingPreference.java

    r1165 r1169  
    2222public class DrawingPreference implements PreferenceSetting {
    2323
    24         private JCheckBox drawRawGpsLines = new JCheckBox(tr("Draw lines between raw gps points."));
    25         private JTextField drawRawGpsMaxLineLength = new JTextField(8);
    26         private JCheckBox forceRawGpsLines = new JCheckBox(tr("Force lines if no segments imported."));
    27         private JCheckBox largeGpsPoints = new JCheckBox(tr("Draw large GPS points."));
    28         private JCheckBox colorTracks = new JCheckBox(tr("Color tracks by velocity."));
    29         private JCheckBox directionHint = new JCheckBox(tr("Draw Direction Arrows"));
    30         private JCheckBox drawGpsArrows = new JCheckBox(tr("Draw Direction Arrows"));
    31         private JCheckBox drawGpsArrowsFast = new JCheckBox(tr("Fast drawing (looks uglier)"));
    32         private JTextField drawGpsArrowsMinDist = new JTextField(8);
    33         private JCheckBox interestingDirections = new JCheckBox(tr("Only interesting direction hints (e.g. with oneway tag)."));
    34         private JCheckBox segmentOrderNumber = new JCheckBox(tr("Draw segment order numbers"));
    35         private JCheckBox sourceBounds = new JCheckBox(tr("Draw boundaries of downloaded data"));
    36         private JCheckBox virtualNodes = new JCheckBox(tr("Draw virtual nodes in select mode"));
    37         private JCheckBox inactive = new JCheckBox(tr("Draw inactive layers in other color"));
    38         private JCheckBox useAntialiasing = new JCheckBox(tr("Smooth map graphics (antialiasing)"));
     24    private JCheckBox drawRawGpsLines = new JCheckBox(tr("Draw lines between raw gps points."));
     25    private JTextField drawRawGpsMaxLineLength = new JTextField(8);
     26    private JCheckBox forceRawGpsLines = new JCheckBox(tr("Force lines if no segments imported."));
     27    private JCheckBox largeGpsPoints = new JCheckBox(tr("Draw large GPS points."));
     28    private JCheckBox colorTracks = new JCheckBox(tr("Color tracks by velocity."));
     29    private JCheckBox directionHint = new JCheckBox(tr("Draw Direction Arrows"));
     30    private JCheckBox drawGpsArrows = new JCheckBox(tr("Draw Direction Arrows"));
     31    private JCheckBox drawGpsArrowsFast = new JCheckBox(tr("Fast drawing (looks uglier)"));
     32    private JTextField drawGpsArrowsMinDist = new JTextField(8);
     33    private JCheckBox interestingDirections = new JCheckBox(tr("Only interesting direction hints (e.g. with oneway tag)."));
     34    private JCheckBox segmentOrderNumber = new JCheckBox(tr("Draw segment order numbers"));
     35    private JCheckBox sourceBounds = new JCheckBox(tr("Draw boundaries of downloaded data"));
     36    private JCheckBox virtualNodes = new JCheckBox(tr("Draw virtual nodes in select mode"));
     37    private JCheckBox inactive = new JCheckBox(tr("Draw inactive layers in other color"));
     38    private JCheckBox useAntialiasing = new JCheckBox(tr("Smooth map graphics (antialiasing)"));
    3939
    40         public void addGui(PreferenceDialog gui) {
    41             gui.display.setPreferredSize(new Dimension(400,600));
     40    public void addGui(PreferenceDialog gui) {
     41        gui.display.setPreferredSize(new Dimension(400,600));
    4242        JPanel panel = new JPanel(new GridBagLayout());
    4343        panel.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
    4444
    45             // drawRawGpsLines
    46                 drawRawGpsLines.addActionListener(new ActionListener(){
    47                         public void actionPerformed(ActionEvent e) {
     45        // drawRawGpsLines
     46        drawRawGpsLines.addActionListener(new ActionListener(){
     47            public void actionPerformed(ActionEvent e) {
    4848                            forceRawGpsLines.setEnabled(drawRawGpsLines.isSelected());
    4949                            drawRawGpsMaxLineLength.setEnabled(drawRawGpsLines.isSelected());
     
    5252                            drawGpsArrowsMinDist.setEnabled(drawGpsArrows.isSelected() && drawGpsArrows.isEnabled());
    5353                            colorTracks.setEnabled(drawRawGpsLines.isSelected());
    54                         }
    55                 });
    56                 drawRawGpsLines.setSelected(Main.pref.getBoolean("draw.rawgps.lines"));
    57                 drawRawGpsLines.setToolTipText(tr("If your gps device draw too few lines, select this to draw lines along your way."));
    58                 panel.add(drawRawGpsLines, GBC.eol().insets(20,0,0,0));
     54            }
     55        });
     56        drawRawGpsLines.setSelected(Main.pref.getBoolean("draw.rawgps.lines"));
     57        drawRawGpsLines.setToolTipText(tr("If your gps device draw too few lines, select this to draw lines along your way."));
     58        panel.add(drawRawGpsLines, GBC.eol().insets(20,0,0,0));
    5959
    60                 // drawRawGpsMaxLineLength
    61                 drawRawGpsMaxLineLength.setText(Integer.toString(Main.pref.getInteger("draw.rawgps.max-line-length", -1)));
    62                 drawRawGpsMaxLineLength.setToolTipText(tr("Maximum length (in meters) to draw lines. Set to '-1' to draw all lines."));
    63                 drawRawGpsMaxLineLength.setEnabled(drawRawGpsLines.isSelected());
    64                 panel.add(new JLabel(tr("Maximum length (meters)")), GBC.std().insets(40,0,0,0));
    65                 panel.add(drawRawGpsMaxLineLength, GBC.eol().fill(GBC.HORIZONTAL).insets(5,0,0,5));
     60        // drawRawGpsMaxLineLength
     61        drawRawGpsMaxLineLength.setText(Integer.toString(Main.pref.getInteger("draw.rawgps.max-line-length", -1)));
     62        drawRawGpsMaxLineLength.setToolTipText(tr("Maximum length (in meters) to draw lines. Set to '-1' to draw all lines."));
     63        drawRawGpsMaxLineLength.setEnabled(drawRawGpsLines.isSelected());
     64        panel.add(new JLabel(tr("Maximum length (meters)")), GBC.std().insets(40,0,0,0));
     65        panel.add(drawRawGpsMaxLineLength, GBC.eol().fill(GBC.HORIZONTAL).insets(5,0,0,5));
    6666
    67                 // forceRawGpsLines
    68                 forceRawGpsLines.setToolTipText(tr("Force drawing of lines if the imported data contain no line information."));
    69                 forceRawGpsLines.setSelected(Main.pref.getBoolean("draw.rawgps.lines.force"));
    70                 forceRawGpsLines.setEnabled(drawRawGpsLines.isSelected());
    71                 panel.add(forceRawGpsLines, GBC.eop().insets(40,0,0,0));
    72                
    73                 // drawGpsArrows
    74                 drawGpsArrows.addActionListener(new ActionListener(){
    75                         public void actionPerformed(ActionEvent e) {
     67        // forceRawGpsLines
     68        forceRawGpsLines.setToolTipText(tr("Force drawing of lines if the imported data contain no line information."));
     69        forceRawGpsLines.setSelected(Main.pref.getBoolean("draw.rawgps.lines.force"));
     70        forceRawGpsLines.setEnabled(drawRawGpsLines.isSelected());
     71        panel.add(forceRawGpsLines, GBC.eop().insets(40,0,0,0));
     72
     73        // drawGpsArrows
     74        drawGpsArrows.addActionListener(new ActionListener(){
     75            public void actionPerformed(ActionEvent e) {
    7676                            drawGpsArrowsFast.setEnabled(drawGpsArrows.isSelected() && drawGpsArrows.isEnabled());
    7777                            drawGpsArrowsMinDist.setEnabled(drawGpsArrows.isSelected() && drawGpsArrows.isEnabled());
    78                         }
    79                 });
    80                 drawGpsArrows.setToolTipText(tr("Draw direction arrows for lines, connecting GPS points."));
    81                 drawGpsArrows.setSelected(Main.pref.getBoolean("draw.rawgps.direction"));
    82                 drawGpsArrows.setEnabled(drawRawGpsLines.isSelected());
    83                 panel.add(drawGpsArrows, GBC.eop().insets(40,0,0,0));
     78            }
     79        });
     80        drawGpsArrows.setToolTipText(tr("Draw direction arrows for lines, connecting GPS points."));
     81        drawGpsArrows.setSelected(Main.pref.getBoolean("draw.rawgps.direction"));
     82        drawGpsArrows.setEnabled(drawRawGpsLines.isSelected());
     83        panel.add(drawGpsArrows, GBC.eop().insets(40,0,0,0));
    8484
    85                 // drawGpsArrowsFast
    86                 drawGpsArrowsFast.setToolTipText(tr("Draw the direction arrows using table lookups instead of complex math."));
    87                 drawGpsArrowsFast.setSelected(Main.pref.getBoolean("draw.rawgps.alternatedirection"));
    88                 drawGpsArrowsFast.setEnabled(drawGpsArrows.isSelected() && drawGpsArrows.isEnabled());
    89                 panel.add(drawGpsArrowsFast, GBC.eop().insets(60,0,0,0));
     85        // drawGpsArrowsFast
     86        drawGpsArrowsFast.setToolTipText(tr("Draw the direction arrows using table lookups instead of complex math."));
     87        drawGpsArrowsFast.setSelected(Main.pref.getBoolean("draw.rawgps.alternatedirection"));
     88        drawGpsArrowsFast.setEnabled(drawGpsArrows.isSelected() && drawGpsArrows.isEnabled());
     89        panel.add(drawGpsArrowsFast, GBC.eop().insets(60,0,0,0));
    9090
    91                 // drawGpsArrowsMinDist
    92                 drawGpsArrowsMinDist.setToolTipText(tr("Don't draw arrows if they are not at least this distance away from the last one."));
    93                 drawGpsArrowsMinDist.setText(Integer.toString(Main.pref.getInteger("draw.rawgps.min-arrow-distance", 0)));
    94                 drawGpsArrowsMinDist.setEnabled(drawGpsArrows.isSelected() && drawGpsArrows.isEnabled());
    95                 panel.add(new JLabel(tr("Minimum distance (pixels)")), GBC.std().insets(60,0,0,0));
    96                 panel.add(drawGpsArrowsMinDist, GBC.eol().fill(GBC.HORIZONTAL).insets(5,0,0,5));
     91        // drawGpsArrowsMinDist
     92        drawGpsArrowsMinDist.setToolTipText(tr("Don't draw arrows if they are not at least this distance away from the last one."));
     93        drawGpsArrowsMinDist.setText(Integer.toString(Main.pref.getInteger("draw.rawgps.min-arrow-distance", 0)));
     94        drawGpsArrowsMinDist.setEnabled(drawGpsArrows.isSelected() && drawGpsArrows.isEnabled());
     95        panel.add(new JLabel(tr("Minimum distance (pixels)")), GBC.std().insets(60,0,0,0));
     96        panel.add(drawGpsArrowsMinDist, GBC.eol().fill(GBC.HORIZONTAL).insets(5,0,0,5));
    9797
    98                 // colorTracks
    99                 colorTracks.setSelected(Main.pref.getBoolean("draw.rawgps.colors"));
    100                 colorTracks.setToolTipText(tr("Choose the hue for the track color by the velocity at that point."));
    101                 colorTracks.setEnabled(drawRawGpsLines.isSelected());
    102                 panel.add(colorTracks, GBC.eop().insets(40,0,0,0));
    103                
    104                 // largeGpsPoints
    105                 largeGpsPoints.setSelected(Main.pref.getBoolean("draw.rawgps.large"));
    106                 largeGpsPoints.setToolTipText(tr("Draw larger dots for the GPS points."));
    107                 panel.add(largeGpsPoints, GBC.eop().insets(20,0,0,0));
    108                
    109                 panel.add(Box.createVerticalGlue(), GBC.eol().fill(GBC.BOTH));
    110                 JScrollPane scrollpane = new JScrollPane(panel);
    111                 scrollpane.setBorder(BorderFactory.createEmptyBorder( 0, 0, 0, 0 ));
    112                 gui.displaycontent.addTab(tr("GPS Points"), scrollpane);
    113                 panel = new JPanel(new GridBagLayout());
    114                 panel.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
     98        // colorTracks
     99        colorTracks.setSelected(Main.pref.getBoolean("draw.rawgps.colors"));
     100        colorTracks.setToolTipText(tr("Choose the hue for the track color by the velocity at that point."));
     101        colorTracks.setEnabled(drawRawGpsLines.isSelected());
     102        panel.add(colorTracks, GBC.eop().insets(40,0,0,0));
    115103
    116                 // directionHint
    117                 directionHint.addActionListener(new ActionListener(){
    118                         public void actionPerformed(ActionEvent e) {
     104        // largeGpsPoints
     105        largeGpsPoints.setSelected(Main.pref.getBoolean("draw.rawgps.large"));
     106        largeGpsPoints.setToolTipText(tr("Draw larger dots for the GPS points."));
     107        panel.add(largeGpsPoints, GBC.eop().insets(20,0,0,0));
     108
     109        panel.add(Box.createVerticalGlue(), GBC.eol().fill(GBC.BOTH));
     110        JScrollPane scrollpane = new JScrollPane(panel);
     111        scrollpane.setBorder(BorderFactory.createEmptyBorder( 0, 0, 0, 0 ));
     112        gui.displaycontent.addTab(tr("GPS Points"), scrollpane);
     113        panel = new JPanel(new GridBagLayout());
     114        panel.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
     115
     116        // directionHint
     117        directionHint.addActionListener(new ActionListener(){
     118            public void actionPerformed(ActionEvent e) {
    119119                            if (directionHint.isSelected()){
    120120                                interestingDirections.setSelected(Main.pref.getBoolean("draw.segment.relevant_directions_only"));
     
    123123                            }
    124124                            interestingDirections.setEnabled(directionHint.isSelected());
    125                         }
    126                 });
    127                 directionHint.setToolTipText(tr("Draw direction hints for way segments."));
    128                 directionHint.setSelected(Main.pref.getBoolean("draw.segment.direction"));
    129                 panel.add(directionHint, GBC.eop().insets(20,0,0,0));
     125            }
     126        });
     127        directionHint.setToolTipText(tr("Draw direction hints for way segments."));
     128        directionHint.setSelected(Main.pref.getBoolean("draw.segment.direction"));
     129        panel.add(directionHint, GBC.eop().insets(20,0,0,0));
    130130
    131                 // only interesting directions
    132                 interestingDirections.setToolTipText(tr("Only interesting direction hints (e.g. with oneway tag)."));
    133                 interestingDirections.setSelected(Main.pref.getBoolean("draw.segment.relevant_directions_only"));
    134                 interestingDirections.setEnabled(directionHint.isSelected());
    135                 panel.add(interestingDirections, GBC.eop().insets(40,0,0,0));
    136                
    137                 // segment order number
    138                 segmentOrderNumber.setToolTipText(tr("Draw the order numbers of all segments within their way."));
    139                 segmentOrderNumber.setSelected(Main.pref.getBoolean("draw.segment.order_number"));
    140                 panel.add(segmentOrderNumber, GBC.eop().insets(20,0,0,0));
    141                
    142                 // antialiasing
    143                 useAntialiasing.setToolTipText(tr("Apply antialiasing to the map view resulting in a smoother appearance."));
    144                 useAntialiasing.setSelected(Main.pref.getBoolean("mappaint.use-antialiasing"));
    145                 panel.add(useAntialiasing, GBC.eop().insets(20,0,0,0));
    146                
    147                 // downloaded area
    148                 sourceBounds.setToolTipText(tr("Draw the boundaries of data loaded from the server."));
    149                 sourceBounds.setSelected(Main.pref.getBoolean("draw.data.downloaded_area", true));
    150                 panel.add(sourceBounds, GBC.eop().insets(20,0,0,0));
     131        // only interesting directions
     132        interestingDirections.setToolTipText(tr("Only interesting direction hints (e.g. with oneway tag)."));
     133        interestingDirections.setSelected(Main.pref.getBoolean("draw.segment.relevant_directions_only"));
     134        interestingDirections.setEnabled(directionHint.isSelected());
     135        panel.add(interestingDirections, GBC.eop().insets(40,0,0,0));
    151136
    152                 // virtual nodes
    153                 virtualNodes.setToolTipText(tr("Draw virtual nodes in select mode for easy way modification."));
    154                 virtualNodes.setSelected(Main.pref.getInteger("mappaint.node.virtual-size", 8) != 0);
    155                 panel.add(virtualNodes, GBC.eop().insets(20,0,0,0));
     137        // segment order number
     138        segmentOrderNumber.setToolTipText(tr("Draw the order numbers of all segments within their way."));
     139        segmentOrderNumber.setSelected(Main.pref.getBoolean("draw.segment.order_number"));
     140        panel.add(segmentOrderNumber, GBC.eop().insets(20,0,0,0));
    156141
    157                 // background layers in inactive color
    158                 inactive.setToolTipText(tr("Draw the inactive data layers in a different color."));
    159                 inactive.setSelected(Main.pref.getBoolean("draw.data.inactive_color", true));
    160                 panel.add(inactive, GBC.eop().insets(20,0,0,0));
     142        // antialiasing
     143        useAntialiasing.setToolTipText(tr("Apply antialiasing to the map view resulting in a smoother appearance."));
     144        useAntialiasing.setSelected(Main.pref.getBoolean("mappaint.use-antialiasing"));
     145        panel.add(useAntialiasing, GBC.eop().insets(20,0,0,0));
    161146
    162                 panel.add(Box.createVerticalGlue(), GBC.eol().fill(GBC.BOTH));
    163                 scrollpane = new JScrollPane(panel);
    164                 scrollpane.setBorder(BorderFactory.createEmptyBorder( 0, 0, 0, 0 ));
    165                 gui.displaycontent.addTab(tr("OSM Data"), scrollpane);
    166         }
     147        // downloaded area
     148        sourceBounds.setToolTipText(tr("Draw the boundaries of data loaded from the server."));
     149        sourceBounds.setSelected(Main.pref.getBoolean("draw.data.downloaded_area", true));
     150        panel.add(sourceBounds, GBC.eop().insets(20,0,0,0));
    167151
    168         public void ok() {
    169                 Main.pref.put("draw.rawgps.lines", drawRawGpsLines.isSelected());
    170                 Main.pref.put("draw.rawgps.max-line-length", drawRawGpsMaxLineLength.getText());
    171                 Main.pref.put("draw.rawgps.lines.force", forceRawGpsLines.isSelected());
    172                 Main.pref.put("draw.rawgps.direction", drawGpsArrows.isSelected());
    173                 Main.pref.put("draw.rawgps.alternatedirection", drawGpsArrowsFast.isSelected());
    174                 Main.pref.put("draw.rawgps.min-arrow-distance", drawGpsArrowsMinDist.getText());
    175                 Main.pref.put("draw.rawgps.colors", colorTracks.isSelected());
    176                 Main.pref.put("draw.rawgps.large", largeGpsPoints.isSelected());
    177                 Main.pref.put("draw.segment.direction", directionHint.isSelected());
    178                 Main.pref.put("draw.segment.relevant_directions_only", interestingDirections.isSelected());
    179                 Main.pref.put("draw.segment.order_number", segmentOrderNumber.isSelected());
    180                 Main.pref.put("draw.data.downloaded_area", sourceBounds.isSelected());
    181                 Main.pref.put("draw.data.inactive_color", inactive.isSelected());
    182                 Main.pref.put("mappaint.use-antialiasing", useAntialiasing.isSelected());
    183                 int vn = Main.pref.getInteger("mappaint.node.virtual-size", 8);
    184                 if(virtualNodes.isSelected()) { if (vn < 1) vn = 8; }
    185                 else { vn = 0; }
    186                 Main.pref.put("mappaint.node.virtual-size", Integer.toString(vn));
    187         }
     152        // virtual nodes
     153        virtualNodes.setToolTipText(tr("Draw virtual nodes in select mode for easy way modification."));
     154        virtualNodes.setSelected(Main.pref.getInteger("mappaint.node.virtual-size", 8) != 0);
     155        panel.add(virtualNodes, GBC.eop().insets(20,0,0,0));
     156
     157        // background layers in inactive color
     158        inactive.setToolTipText(tr("Draw the inactive data layers in a different color."));
     159        inactive.setSelected(Main.pref.getBoolean("draw.data.inactive_color", true));
     160        panel.add(inactive, GBC.eop().insets(20,0,0,0));
     161
     162        panel.add(Box.createVerticalGlue(), GBC.eol().fill(GBC.BOTH));
     163        scrollpane = new JScrollPane(panel);
     164        scrollpane.setBorder(BorderFactory.createEmptyBorder( 0, 0, 0, 0 ));
     165        gui.displaycontent.addTab(tr("OSM Data"), scrollpane);
     166    }
     167
     168    public void ok() {
     169        Main.pref.put("draw.rawgps.lines", drawRawGpsLines.isSelected());
     170        Main.pref.put("draw.rawgps.max-line-length", drawRawGpsMaxLineLength.getText());
     171        Main.pref.put("draw.rawgps.lines.force", forceRawGpsLines.isSelected());
     172        Main.pref.put("draw.rawgps.direction", drawGpsArrows.isSelected());
     173        Main.pref.put("draw.rawgps.alternatedirection", drawGpsArrowsFast.isSelected());
     174        Main.pref.put("draw.rawgps.min-arrow-distance", drawGpsArrowsMinDist.getText());
     175        Main.pref.put("draw.rawgps.colors", colorTracks.isSelected());
     176        Main.pref.put("draw.rawgps.large", largeGpsPoints.isSelected());
     177        Main.pref.put("draw.segment.direction", directionHint.isSelected());
     178        Main.pref.put("draw.segment.relevant_directions_only", interestingDirections.isSelected());
     179        Main.pref.put("draw.segment.order_number", segmentOrderNumber.isSelected());
     180        Main.pref.put("draw.data.downloaded_area", sourceBounds.isSelected());
     181        Main.pref.put("draw.data.inactive_color", inactive.isSelected());
     182        Main.pref.put("mappaint.use-antialiasing", useAntialiasing.isSelected());
     183        int vn = Main.pref.getInteger("mappaint.node.virtual-size", 8);
     184        if(virtualNodes.isSelected()) { if (vn < 1) vn = 8; }
     185        else { vn = 0; }
     186        Main.pref.put("mappaint.node.virtual-size", Integer.toString(vn));
     187    }
    188188}
  • trunk/src/org/openstreetmap/josm/gui/preferences/FilePreferences.java

    r999 r1169  
    1414 * Out of pure laziness, I add the file stuff to connection tab.
    1515 * Feel free to fix this.
    16  * 
     16 *
    1717 * @author imi
    1818 */
    1919public class FilePreferences implements PreferenceSetting {
    2020
    21         private JCheckBox keepBackup = new JCheckBox(tr("Keep backup files"));
    22        
    23         public void addGui(PreferenceDialog gui) {
    24                 gui.connection.add(new JSeparator(SwingConstants.HORIZONTAL), GBC.eol().fill(GBC.HORIZONTAL));
    25                 keepBackup.setSelected(Main.pref.getBoolean("save.keepbackup"));
    26                 keepBackup.setToolTipText(tr("When saving, keep backup files ending with a ~"));
    27                 gui.connection.add(keepBackup, GBC.eol().insets(20,0,0,0));
     21    private JCheckBox keepBackup = new JCheckBox(tr("Keep backup files"));
     22
     23    public void addGui(PreferenceDialog gui) {
     24        gui.connection.add(new JSeparator(SwingConstants.HORIZONTAL), GBC.eol().fill(GBC.HORIZONTAL));
     25        keepBackup.setSelected(Main.pref.getBoolean("save.keepbackup"));
     26        keepBackup.setToolTipText(tr("When saving, keep backup files ending with a ~"));
     27        gui.connection.add(keepBackup, GBC.eol().insets(20,0,0,0));
    2828    }
    2929
    30         public void ok() {
    31                 Main.pref.put("save.keepbackup", keepBackup.isSelected());
     30    public void ok() {
     31        Main.pref.put("save.keepbackup", keepBackup.isSelected());
    3232    }
    3333}
  • trunk/src/org/openstreetmap/josm/gui/preferences/LafPreference.java

    r1165 r1169  
    2525public class LafPreference implements PreferenceSetting {
    2626
    27         /**
    28         * ComboBox with all look and feels.
    29         */
    30         private JComboBox lafCombo;
    31         public JPanel panel = new JPanel(new GridBagLayout());
    32         private JCheckBox showSplashScreen = new JCheckBox(tr("Show splash screen at startup"));
    33         private JCheckBox showID = new JCheckBox(tr("Show object ID in selection lists"));
     27    /**
     28    * ComboBox with all look and feels.
     29    */
     30    private JComboBox lafCombo;
     31    public JPanel panel = new JPanel(new GridBagLayout());
     32    private JCheckBox showSplashScreen = new JCheckBox(tr("Show splash screen at startup"));
     33    private JCheckBox showID = new JCheckBox(tr("Show object ID in selection lists"));
    3434
    35         public void addGui(PreferenceDialog gui) {
    36                 lafCombo = new JComboBox(UIManager.getInstalledLookAndFeels());
     35    public void addGui(PreferenceDialog gui) {
     36        lafCombo = new JComboBox(UIManager.getInstalledLookAndFeels());
    3737
    38                 // let's try to load additional LookAndFeels and put them into the list
    39                 try {
    40                         Class<?> Cquaqua = Class.forName("ch.randelshofer.quaqua.QuaquaLookAndFeel");
    41                         Object Oquaqua = Cquaqua.getConstructor((Class[])null).newInstance((Object[])null);
    42                         // no exception? Then Go!
    43                         lafCombo.addItem(
    44                                 new UIManager.LookAndFeelInfo(((javax.swing.LookAndFeel)Oquaqua).getName(), "ch.randelshofer.quaqua.QuaquaLookAndFeel")
    45                         );
    46                 } catch (Exception ex) {
    47                         // just ignore, Quaqua may not even be installed...
    48                         //System.out.println("Failed to load Quaqua: " + ex);
    49                 }
     38        // let's try to load additional LookAndFeels and put them into the list
     39        try {
     40            Class<?> Cquaqua = Class.forName("ch.randelshofer.quaqua.QuaquaLookAndFeel");
     41            Object Oquaqua = Cquaqua.getConstructor((Class[])null).newInstance((Object[])null);
     42            // no exception? Then Go!
     43            lafCombo.addItem(
     44                new UIManager.LookAndFeelInfo(((javax.swing.LookAndFeel)Oquaqua).getName(), "ch.randelshofer.quaqua.QuaquaLookAndFeel")
     45            );
     46        } catch (Exception ex) {
     47            // just ignore, Quaqua may not even be installed...
     48            //System.out.println("Failed to load Quaqua: " + ex);
     49        }
    5050
    51                 String laf = Main.pref.get("laf");
    52                 for (int i = 0; i < lafCombo.getItemCount(); ++i) {
    53                         if (((LookAndFeelInfo)lafCombo.getItemAt(i)).getClassName().equals(laf)) {
    54                                 lafCombo.setSelectedIndex(i);
    55                                 break;
    56                         }
    57                 }
     51        String laf = Main.pref.get("laf");
     52        for (int i = 0; i < lafCombo.getItemCount(); ++i) {
     53            if (((LookAndFeelInfo)lafCombo.getItemAt(i)).getClassName().equals(laf)) {
     54                lafCombo.setSelectedIndex(i);
     55                break;
     56            }
     57        }
    5858
    59                 final ListCellRenderer oldRenderer = lafCombo.getRenderer();
    60                 lafCombo.setRenderer(new DefaultListCellRenderer(){
    61                         @Override public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
    62                                 return oldRenderer.getListCellRendererComponent(list, ((LookAndFeelInfo)value).getName(), index, isSelected, cellHasFocus);
    63                         }
    64                 });
    65                 lafCombo.addActionListener(gui.requireRestartAction);
     59        final ListCellRenderer oldRenderer = lafCombo.getRenderer();
     60        lafCombo.setRenderer(new DefaultListCellRenderer(){
     61            @Override public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
     62                return oldRenderer.getListCellRendererComponent(list, ((LookAndFeelInfo)value).getName(), index, isSelected, cellHasFocus);
     63            }
     64        });
     65        lafCombo.addActionListener(gui.requireRestartAction);
    6666
    67                 panel.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
     67        panel.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
    6868
    69                 // Show splash screen on startup
    70                 showSplashScreen.setToolTipText(tr("Show splash screen at startup"));
    71                 showSplashScreen.setSelected(Main.pref.getBoolean("draw.splashscreen", true));
    72                 panel.add(showSplashScreen, GBC.eop().insets(20, 0, 0, 0));
     69        // Show splash screen on startup
     70        showSplashScreen.setToolTipText(tr("Show splash screen at startup"));
     71        showSplashScreen.setSelected(Main.pref.getBoolean("draw.splashscreen", true));
     72        panel.add(showSplashScreen, GBC.eop().insets(20, 0, 0, 0));
    7373
    74                 // Show ID in selection
    75                 showID.setToolTipText(tr("Show object ID in selection lists"));
    76                 showID.setSelected(Main.pref.getBoolean("osm-primitives.showid", false));
    77                 panel.add(showID, GBC.eop().insets(20, 0, 0, 0));
     74        // Show ID in selection
     75        showID.setToolTipText(tr("Show object ID in selection lists"));
     76        showID.setSelected(Main.pref.getBoolean("osm-primitives.showid", false));
     77        panel.add(showID, GBC.eop().insets(20, 0, 0, 0));
    7878
    79                 panel.add(Box.createVerticalGlue(), GBC.eol().insets(0, 20, 0, 0));
     79        panel.add(Box.createVerticalGlue(), GBC.eol().insets(0, 20, 0, 0));
    8080
    81                 panel.add(new JLabel(tr("Look and Feel")), GBC.std().insets(20, 0, 0, 0));
    82                 panel.add(GBC.glue(5,0), GBC.std().fill(GBC.HORIZONTAL));
    83                 panel.add(lafCombo, GBC.eol().fill(GBC.HORIZONTAL));
     81        panel.add(new JLabel(tr("Look and Feel")), GBC.std().insets(20, 0, 0, 0));
     82        panel.add(GBC.glue(5,0), GBC.std().fill(GBC.HORIZONTAL));
     83        panel.add(lafCombo, GBC.eol().fill(GBC.HORIZONTAL));
    8484
    85                 JScrollPane scrollpane = new JScrollPane(panel);
    86                 scrollpane.setBorder(BorderFactory.createEmptyBorder( 0, 0, 0, 0 ));
    87                 gui.displaycontent.addTab(tr("Look and Feel"), scrollpane);
    88         }
     85        JScrollPane scrollpane = new JScrollPane(panel);
     86        scrollpane.setBorder(BorderFactory.createEmptyBorder( 0, 0, 0, 0 ));
     87        gui.displaycontent.addTab(tr("Look and Feel"), scrollpane);
     88    }
    8989
    90         public void ok() {
    91                 Main.pref.put("laf", ((LookAndFeelInfo)lafCombo.getSelectedItem()).getClassName());
    92                 Main.pref.put("draw.splashscreen", showSplashScreen.isSelected());
    93                 Main.pref.put("osm-primitives.showid", showID.isSelected());
     90    public void ok() {
     91        Main.pref.put("laf", ((LookAndFeelInfo)lafCombo.getSelectedItem()).getClassName());
     92        Main.pref.put("draw.splashscreen", showSplashScreen.isSelected());
     93        Main.pref.put("osm-primitives.showid", showID.isSelected());
    9494    }
    9595
  • trunk/src/org/openstreetmap/josm/gui/preferences/LanguagePreference.java

    r1165 r1169  
    2020
    2121public class LanguagePreference implements PreferenceSetting {
    22         /**
    23         * ComboBox with all available Translations
    24         */
    25         private JComboBox langCombo;
    26         private final Locale AUTO_LANGUAGE = null;
     22    /**
     23    * ComboBox with all available Translations
     24    */
     25    private JComboBox langCombo;
     26    private final Locale AUTO_LANGUAGE = null;
    2727
    28         public void addGui(PreferenceDialog gui) {
    29                 langCombo = new JComboBox(I18n.getAvailableTranslations());
    30                 langCombo.insertItemAt(AUTO_LANGUAGE, 0); // Default
    31                 langCombo.insertItemAt(Locale.ENGLISH, 1); // Built-in language
    32                 String ln = Main.pref.get("language");
    33                 langCombo.setSelectedIndex(0);
    34                
    35                 if (ln != null) {
    36                         for (int i = 1; i < langCombo.getItemCount(); ++i) {
    37                                 if (((Locale) langCombo.getItemAt(i)).toString().equals(ln)) {
    38                                         langCombo.setSelectedIndex(i);
    39                                         break;
    40                                 }
    41                         }
    42                 }
     28    public void addGui(PreferenceDialog gui) {
     29        langCombo = new JComboBox(I18n.getAvailableTranslations());
     30        langCombo.insertItemAt(AUTO_LANGUAGE, 0); // Default
     31        langCombo.insertItemAt(Locale.ENGLISH, 1); // Built-in language
     32        String ln = Main.pref.get("language");
     33        langCombo.setSelectedIndex(0);
    4334
    44                 final ListCellRenderer oldRenderer = langCombo.getRenderer();
    45                 langCombo.setRenderer(new DefaultListCellRenderer() {
    46                         @Override
    47                         public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected,
    48                                         boolean cellHasFocus) {
    49                                 Locale l = (Locale) value;
    50                                 return oldRenderer.getListCellRendererComponent(list,
    51                                                 l == AUTO_LANGUAGE ? tr("Default (Auto determined)") : l.getDisplayName(),
    52                                                 index, isSelected, cellHasFocus);
    53                         }
    54                 });
    55                 langCombo.addActionListener(gui.requireRestartAction);
     35        if (ln != null) {
     36            for (int i = 1; i < langCombo.getItemCount(); ++i) {
     37                if (((Locale) langCombo.getItemAt(i)).toString().equals(ln)) {
     38                    langCombo.setSelectedIndex(i);
     39                    break;
     40                }
     41            }
     42        }
    5643
    57                 JPanel panel = null;
    58                 for(PreferenceSetting s : gui.settings)
    59                 {
    60                         if(s instanceof LafPreference)
    61                                 panel = ((LafPreference)s).panel;
    62                 }
    63                 panel.add(new JLabel(tr("Language")), GBC.std().insets(20, 0, 0, 0));
    64                 panel.add(GBC.glue(5,0), GBC.std().fill(GBC.HORIZONTAL));
    65                 panel.add(langCombo, GBC.eol().fill(GBC.HORIZONTAL));
    66                 panel.add(Box.createVerticalGlue(), GBC.eol().fill(GBC.BOTH));
    67         }
     44        final ListCellRenderer oldRenderer = langCombo.getRenderer();
     45        langCombo.setRenderer(new DefaultListCellRenderer() {
     46            @Override
     47            public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected,
     48                    boolean cellHasFocus) {
     49                Locale l = (Locale) value;
     50                return oldRenderer.getListCellRendererComponent(list,
     51                        l == AUTO_LANGUAGE ? tr("Default (Auto determined)") : l.getDisplayName(),
     52                        index, isSelected, cellHasFocus);
     53            }
     54        });
     55        langCombo.addActionListener(gui.requireRestartAction);
    6856
    69         public void ok() {
    70                 if(langCombo.getSelectedItem() == null)
    71                 {
    72                         Main.pref.put("language", null);
    73                 }
    74                 else
    75                 {
    76                         String l = ((Locale)langCombo.getSelectedItem()).toString();
    77                         Main.pref.put("language", l);
    78                 }
    79         }
     57        JPanel panel = null;
     58        for(PreferenceSetting s : gui.settings)
     59        {
     60            if(s instanceof LafPreference)
     61                panel = ((LafPreference)s).panel;
     62        }
     63        panel.add(new JLabel(tr("Language")), GBC.std().insets(20, 0, 0, 0));
     64        panel.add(GBC.glue(5,0), GBC.std().fill(GBC.HORIZONTAL));
     65        panel.add(langCombo, GBC.eol().fill(GBC.HORIZONTAL));
     66        panel.add(Box.createVerticalGlue(), GBC.eol().fill(GBC.BOTH));
     67    }
     68
     69    public void ok() {
     70        if(langCombo.getSelectedItem() == null)
     71        {
     72            Main.pref.put("language", null);
     73        }
     74        else
     75        {
     76            String l = ((Locale)langCombo.getSelectedItem()).toString();
     77            Main.pref.put("language", l);
     78        }
     79    }
    8080}
  • trunk/src/org/openstreetmap/josm/gui/preferences/MapPaintPreference.java

    r627 r1169  
    55
    66public class MapPaintPreference implements PreferenceSetting {
    7        
    8         public void addGui(final PreferenceDialog gui) {
    9                 // this is intended for a future configuration panel for mappaint!
    10         }
    117
    12         public void ok() {
    13                 // dummy
    14         }
     8    public void addGui(final PreferenceDialog gui) {
     9        // this is intended for a future configuration panel for mappaint!
     10    }
    1511
    16         /**
    17          * Initialize the styles
    18          */
    19         public static void initialize() {
    20                 MapPaintStyles.readFromPreferences();
    21         }
     12    public void ok() {
     13        // dummy
     14    }
     15
     16    /**
     17     * Initialize the styles
     18     */
     19    public static void initialize() {
     20        MapPaintStyles.readFromPreferences();
     21    }
    2222}
  • trunk/src/org/openstreetmap/josm/gui/preferences/PluginPreference.java

    r1133 r1169  
    4949public class PluginPreference implements PreferenceSetting {
    5050
    51         /**
    52         * Only the plugin name, its jar location and the description.
    53         * In other words, this is the minimal requirement the plugin preference page
    54         * needs to show the plugin as available
    55          *
    56         * @author imi
    57         */
    58         public static class PluginDescription implements Comparable<Object> {
    59                 // Note: All the following need to be public instance variables of
    60                 // type String.  (Plugin description XMLs from the server are parsed
    61                 // with tools.XmlObjectParser, which uses reflection to access them.)
    62                 public String name;
    63                 public String description;
    64                 public String resource;
    65                 public String version;
    66                 public PluginDescription(String name, String description, String resource, String version) {
    67                         this.name = name;
    68                         this.description = description;
    69                         this.resource = resource;
    70                         this.version = version;
    71                 }
    72                 public PluginDescription() {
    73                 }
    74                 public int compareTo(Object n) {
    75                         if(n instanceof PluginDescription)
    76                                 return name.compareToIgnoreCase(((PluginDescription)n).name);
    77                         return -1;
    78                 }
    79         }
    80 
    81         private Map<PluginDescription, Boolean> pluginMap;
    82         private JPanel plugin;
    83         private class MyBox extends Box {
    84                 int lastwidth;
    85                 int offset = 40;
    86                 public MyBox()
    87                 {
    88                         super(BoxLayout.Y_AXIS);
    89                 }
    90                 public int myGetWidth()
    91                 {
    92                         int w = plugin.getWidth()-offset;
    93                         if(w <= 0) w = 450;
    94                         lastwidth = w;
    95                         return w;
    96                 }
    97                 public void paint(Graphics g)
    98                 {
    99                         if(lastwidth != plugin.getWidth()-offset)
    100                                 refreshPluginPanel(gui);
    101                         super.paint(g);
    102                 }
    103         }
    104         private MyBox pluginPanel = new MyBox();
    105         private PreferenceDialog gui;
    106 
    107         public void addGui(final PreferenceDialog gui) {
    108                 this.gui = gui;
    109                 plugin = gui.createPreferenceTab("plugin", tr("Plugins"), tr("Configure available plugins."), false);
    110                 JScrollPane pluginPane = new JScrollPane(pluginPanel, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
    111                 pluginPane.setBorder(null);
    112                 plugin.add(pluginPane, GBC.eol().fill(GBC.BOTH));
    113                 plugin.add(GBC.glue(0,10), GBC.eol());
    114                 JButton morePlugins = new JButton(tr("Download List"));
    115                 morePlugins.addActionListener(new ActionListener(){
    116                     public void actionPerformed(ActionEvent e) {
    117                         int count = PluginDownloader.downloadDescription();
    118                         if (count > 0)
    119                             JOptionPane.showMessageDialog(Main.parent,
    120                                 trn("Downloaded plugin information from {0} site",
    121                                     "Downloaded plugin information from {0} sites", count, count));
    122                         else
    123                             JOptionPane.showMessageDialog(Main.parent, tr("No plugin information found."));
    124                         refreshPluginPanel(gui);
    125                         }
    126                 });
    127                 plugin.add(morePlugins, GBC.std().insets(0,0,10,0));
    128 
    129                 JButton update = new JButton(tr("Update"));
    130                 update.addActionListener(new ActionListener(){
    131                         public void actionPerformed(ActionEvent e) {
    132                                 update();
    133                                 refreshPluginPanel(gui);
    134                         }
    135                 });
    136                 plugin.add(update, GBC.std().insets(0,0,10,0));
    137 
    138                 JButton configureSites = new JButton(tr("Configure Sites ..."));
    139                 configureSites.addActionListener(new ActionListener(){
    140                         public void actionPerformed(ActionEvent e) {
    141                                 configureSites();
    142                         }
    143                 });
    144                 plugin.add(configureSites, GBC.std());
    145 
    146                 refreshPluginPanel(gui);
    147         }
    148 
    149         private void configureSites() {
    150                 JPanel p = new JPanel(new GridBagLayout());
    151                 p.add(new JLabel(tr("Add either site-josm.xml or Wiki Pages.")), GBC.eol());
    152                 final DefaultListModel model = new DefaultListModel();
    153                 for (String s : PluginDownloader.getSites())
    154                         model.addElement(s);
    155                 final JList list = new JList(model);
    156                 p.add(new JScrollPane(list), GBC.std().fill());
    157                 JPanel buttons = new JPanel(new GridBagLayout());
    158                 buttons.add(new JButton(new AbstractAction(tr("Add")){
    159                         public void actionPerformed(ActionEvent e) {
    160                                 String s = JOptionPane.showInputDialog(gui, tr("Add either site-josm.xml or Wiki Pages."));
    161                                 if (s != null)
    162                                         model.addElement(s);
    163             }
    164                 }), GBC.eol().fill(GBC.HORIZONTAL));
    165                 buttons.add(new JButton(new AbstractAction(tr("Edit")){
    166                         public void actionPerformed(ActionEvent e) {
    167                                 if (list.getSelectedValue() == null) {
    168                                         JOptionPane.showMessageDialog(gui, tr("Please select an entry."));
    169                                         return;
    170                                 }
    171                                 String s = JOptionPane.showInputDialog(gui, tr("Add either site-josm.xml or Wiki Pages."), list.getSelectedValue());
    172                                 model.setElementAt(s, list.getSelectedIndex());
    173             }
    174                 }), GBC.eol().fill(GBC.HORIZONTAL));
    175                 buttons.add(new JButton(new AbstractAction(tr("Delete")){
    176                         public void actionPerformed(ActionEvent event) {
    177                                 if (list.getSelectedValue() == null) {
    178                                         JOptionPane.showMessageDialog(gui, tr("Please select an entry."));
    179                                         return;
    180                                 }
    181                                 model.removeElement(list.getSelectedValue());
    182             }
    183                 }), GBC.eol().fill(GBC.HORIZONTAL));
    184                 p.add(buttons, GBC.eol());
    185                 int answer = JOptionPane.showConfirmDialog(gui, p, tr("Configure Plugin Sites"), JOptionPane.OK_CANCEL_OPTION);
    186                 if (answer != JOptionPane.OK_OPTION)
    187                         return;
    188                 StringBuilder b = new StringBuilder();
    189                 for (int i = 0; i < model.getSize(); ++i) {
    190                         b.append(model.getElementAt(i));
    191                         if (i < model.getSize()-1)
    192                                 b.append(" ");
    193                 }
    194                 Main.pref.put("pluginmanager.sites", b.toString());
    195         }
    196 
    197         private void update() {
    198                 // refresh description
    199                 int num = PluginDownloader.downloadDescription();
    200                 Boolean done = false;
    201                 refreshPluginPanel(gui);
    202 
    203                 Set<PluginDescription> toUpdate = new HashSet<PluginDescription>();
    204                 StringBuilder toUpdateStr = new StringBuilder();
    205                 for (PluginProxy proxy : Main.plugins) {
    206                         PluginDescription description = findDescription(proxy.info.name);
    207                         if (description != null && (description.version == null || description.version.equals(""))
    208                         ? (proxy.info.version != null && proxy.info.version.equals("")) : !description.version.equals(proxy.info.version)) {
    209                                 toUpdate.add(description);
    210                                 toUpdateStr.append(description.name+"\n");
    211                         }
    212                 }
    213                 if (toUpdate.isEmpty()) {
    214                         JOptionPane.showMessageDialog(Main.parent, tr("All installed plugins are up to date."));
    215                         done = true;
    216                 }
    217                 else
    218                 {
    219                         int answer = JOptionPane.showConfirmDialog(Main.parent, tr("Update the following plugins:\n\n{0}",
    220                         toUpdateStr.toString()), tr("Update"), JOptionPane.OK_CANCEL_OPTION);
    221                         if (answer == JOptionPane.OK_OPTION)
    222                         {
    223                                 PluginDownloader.update(toUpdate);
    224                                 done = true;
    225                         }
    226                 }
    227                 if(done && num >= 1)
    228                         Main.pref.put("pluginmanager.lastupdate", Long.toString(System.currentTimeMillis()));
    229         }
    230 
    231         private PluginDescription findDescription(String name) {
    232                 for (PluginDescription d : pluginMap.keySet())
    233                         if (d.name.equals(name))
    234                                 return d;
    235                 return null;
    236         }
    237 
    238         private void refreshPluginPanel(final PreferenceDialog gui) {
    239                 Collection<PluginDescription> availablePlugins = getAvailablePlugins();
    240                 pluginMap = new HashMap<PluginDescription, Boolean>();
    241                 pluginPanel.removeAll();
    242                 int width = pluginPanel.myGetWidth();
    243 
    244                 Collection<String> enabledPlugins = Main.pref.getCollection("plugins", null);
    245                
    246                 for (final PluginDescription plugin : availablePlugins) {
    247                         boolean enabled = enabledPlugins != null && enabledPlugins.contains(plugin.name);
    248                         String remoteversion = plugin.version;
    249                         if(remoteversion == null || remoteversion.equals(""))
    250                                 remoteversion = tr("unknown");
    251 
    252                         String localversion;
    253                         PluginInformation p = PluginInformation.findPlugin(plugin.name);
    254                         if(p != null)
    255                         {
    256                                 if(p.version != null && !p.version.equals(""))
    257                                         localversion = p.version;
    258                                 else
    259                                         localversion = tr("unknown");
    260                                 localversion = " (" + localversion + ")";
    261                         }
    262                         else
    263                                 localversion = "";
    264 
    265                         final JCheckBox pluginCheck = new JCheckBox(tr("{0}: Version {1}{2}", plugin.name, remoteversion, localversion), enabled);
    266                         pluginPanel.add(pluginCheck);
    267 
    268                         pluginCheck.setToolTipText(plugin.resource != null ? ""+plugin.resource : tr("Plugin bundled with JOSM"));
    269                         JLabel label = new JLabel("<html><i>"+(plugin.description==null?tr("no description available"):plugin.description)+"</i></html>");
    270                         label.setBorder(BorderFactory.createEmptyBorder(0,20,0,0));
    271                         label.setMaximumSize(new Dimension(width,1000));
    272                         pluginPanel.add(label);
    273                         pluginPanel.add(Box.createVerticalStrut(5));
    274 
    275                         pluginMap.put(plugin, enabled);
    276                         pluginCheck.addActionListener(new ActionListener(){
    277                                 public void actionPerformed(ActionEvent e) {
     51    /**
     52    * Only the plugin name, its jar location and the description.
     53    * In other words, this is the minimal requirement the plugin preference page
     54    * needs to show the plugin as available
     55     *
     56    * @author imi
     57    */
     58    public static class PluginDescription implements Comparable<Object> {
     59        // Note: All the following need to be public instance variables of
     60        // type String.  (Plugin description XMLs from the server are parsed
     61        // with tools.XmlObjectParser, which uses reflection to access them.)
     62        public String name;
     63        public String description;
     64        public String resource;
     65        public String version;
     66        public PluginDescription(String name, String description, String resource, String version) {
     67            this.name = name;
     68            this.description = description;
     69            this.resource = resource;
     70            this.version = version;
     71        }
     72        public PluginDescription() {
     73        }
     74        public int compareTo(Object n) {
     75            if(n instanceof PluginDescription)
     76                return name.compareToIgnoreCase(((PluginDescription)n).name);
     77            return -1;
     78        }
     79    }
     80
     81    private Map<PluginDescription, Boolean> pluginMap;
     82    private JPanel plugin;
     83    private class MyBox extends Box {
     84        int lastwidth;
     85        int offset = 40;
     86        public MyBox()
     87        {
     88            super(BoxLayout.Y_AXIS);
     89        }
     90        public int myGetWidth()
     91        {
     92            int w = plugin.getWidth()-offset;
     93            if(w <= 0) w = 450;
     94            lastwidth = w;
     95            return w;
     96        }
     97        public void paint(Graphics g)
     98        {
     99            if(lastwidth != plugin.getWidth()-offset)
     100                refreshPluginPanel(gui);
     101            super.paint(g);
     102        }
     103    }
     104    private MyBox pluginPanel = new MyBox();
     105    private PreferenceDialog gui;
     106
     107    public void addGui(final PreferenceDialog gui) {
     108        this.gui = gui;
     109        plugin = gui.createPreferenceTab("plugin", tr("Plugins"), tr("Configure available plugins."), false);
     110        JScrollPane pluginPane = new JScrollPane(pluginPanel, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
     111        pluginPane.setBorder(null);
     112        plugin.add(pluginPane, GBC.eol().fill(GBC.BOTH));
     113        plugin.add(GBC.glue(0,10), GBC.eol());
     114        JButton morePlugins = new JButton(tr("Download List"));
     115        morePlugins.addActionListener(new ActionListener(){
     116            public void actionPerformed(ActionEvent e) {
     117                int count = PluginDownloader.downloadDescription();
     118                if (count > 0)
     119                    JOptionPane.showMessageDialog(Main.parent,
     120                        trn("Downloaded plugin information from {0} site",
     121                            "Downloaded plugin information from {0} sites", count, count));
     122                else
     123                    JOptionPane.showMessageDialog(Main.parent, tr("No plugin information found."));
     124                refreshPluginPanel(gui);
     125            }
     126        });
     127        plugin.add(morePlugins, GBC.std().insets(0,0,10,0));
     128
     129        JButton update = new JButton(tr("Update"));
     130        update.addActionListener(new ActionListener(){
     131            public void actionPerformed(ActionEvent e) {
     132                update();
     133                refreshPluginPanel(gui);
     134            }
     135        });
     136        plugin.add(update, GBC.std().insets(0,0,10,0));
     137
     138        JButton configureSites = new JButton(tr("Configure Sites ..."));
     139        configureSites.addActionListener(new ActionListener(){
     140            public void actionPerformed(ActionEvent e) {
     141                configureSites();
     142            }
     143        });
     144        plugin.add(configureSites, GBC.std());
     145
     146        refreshPluginPanel(gui);
     147    }
     148
     149    private void configureSites() {
     150        JPanel p = new JPanel(new GridBagLayout());
     151        p.add(new JLabel(tr("Add either site-josm.xml or Wiki Pages.")), GBC.eol());
     152        final DefaultListModel model = new DefaultListModel();
     153        for (String s : PluginDownloader.getSites())
     154            model.addElement(s);
     155        final JList list = new JList(model);
     156        p.add(new JScrollPane(list), GBC.std().fill());
     157        JPanel buttons = new JPanel(new GridBagLayout());
     158        buttons.add(new JButton(new AbstractAction(tr("Add")){
     159            public void actionPerformed(ActionEvent e) {
     160                String s = JOptionPane.showInputDialog(gui, tr("Add either site-josm.xml or Wiki Pages."));
     161                if (s != null)
     162                    model.addElement(s);
     163            }
     164        }), GBC.eol().fill(GBC.HORIZONTAL));
     165        buttons.add(new JButton(new AbstractAction(tr("Edit")){
     166            public void actionPerformed(ActionEvent e) {
     167                if (list.getSelectedValue() == null) {
     168                    JOptionPane.showMessageDialog(gui, tr("Please select an entry."));
     169                    return;
     170                }
     171                String s = JOptionPane.showInputDialog(gui, tr("Add either site-josm.xml or Wiki Pages."), list.getSelectedValue());
     172                model.setElementAt(s, list.getSelectedIndex());
     173            }
     174        }), GBC.eol().fill(GBC.HORIZONTAL));
     175        buttons.add(new JButton(new AbstractAction(tr("Delete")){
     176            public void actionPerformed(ActionEvent event) {
     177                if (list.getSelectedValue() == null) {
     178                    JOptionPane.showMessageDialog(gui, tr("Please select an entry."));
     179                    return;
     180                }
     181                model.removeElement(list.getSelectedValue());
     182            }
     183        }), GBC.eol().fill(GBC.HORIZONTAL));
     184        p.add(buttons, GBC.eol());
     185        int answer = JOptionPane.showConfirmDialog(gui, p, tr("Configure Plugin Sites"), JOptionPane.OK_CANCEL_OPTION);
     186        if (answer != JOptionPane.OK_OPTION)
     187            return;
     188        StringBuilder b = new StringBuilder();
     189        for (int i = 0; i < model.getSize(); ++i) {
     190            b.append(model.getElementAt(i));
     191            if (i < model.getSize()-1)
     192                b.append(" ");
     193        }
     194        Main.pref.put("pluginmanager.sites", b.toString());
     195    }
     196
     197    private void update() {
     198        // refresh description
     199        int num = PluginDownloader.downloadDescription();
     200        Boolean done = false;
     201        refreshPluginPanel(gui);
     202
     203        Set<PluginDescription> toUpdate = new HashSet<PluginDescription>();
     204        StringBuilder toUpdateStr = new StringBuilder();
     205        for (PluginProxy proxy : Main.plugins) {
     206            PluginDescription description = findDescription(proxy.info.name);
     207            if (description != null && (description.version == null || description.version.equals(""))
     208            ? (proxy.info.version != null && proxy.info.version.equals("")) : !description.version.equals(proxy.info.version)) {
     209                toUpdate.add(description);
     210                toUpdateStr.append(description.name+"\n");
     211            }
     212        }
     213        if (toUpdate.isEmpty()) {
     214            JOptionPane.showMessageDialog(Main.parent, tr("All installed plugins are up to date."));
     215            done = true;
     216        }
     217        else
     218        {
     219            int answer = JOptionPane.showConfirmDialog(Main.parent, tr("Update the following plugins:\n\n{0}",
     220            toUpdateStr.toString()), tr("Update"), JOptionPane.OK_CANCEL_OPTION);
     221            if (answer == JOptionPane.OK_OPTION)
     222            {
     223                PluginDownloader.update(toUpdate);
     224                done = true;
     225            }
     226        }
     227        if(done && num >= 1)
     228            Main.pref.put("pluginmanager.lastupdate", Long.toString(System.currentTimeMillis()));
     229    }
     230
     231    private PluginDescription findDescription(String name) {
     232        for (PluginDescription d : pluginMap.keySet())
     233            if (d.name.equals(name))
     234                return d;
     235        return null;
     236    }
     237
     238    private void refreshPluginPanel(final PreferenceDialog gui) {
     239        Collection<PluginDescription> availablePlugins = getAvailablePlugins();
     240        pluginMap = new HashMap<PluginDescription, Boolean>();
     241        pluginPanel.removeAll();
     242        int width = pluginPanel.myGetWidth();
     243
     244        Collection<String> enabledPlugins = Main.pref.getCollection("plugins", null);
     245
     246        for (final PluginDescription plugin : availablePlugins) {
     247            boolean enabled = enabledPlugins != null && enabledPlugins.contains(plugin.name);
     248            String remoteversion = plugin.version;
     249            if(remoteversion == null || remoteversion.equals(""))
     250                remoteversion = tr("unknown");
     251
     252            String localversion;
     253            PluginInformation p = PluginInformation.findPlugin(plugin.name);
     254            if(p != null)
     255            {
     256                if(p.version != null && !p.version.equals(""))
     257                    localversion = p.version;
     258                else
     259                    localversion = tr("unknown");
     260                localversion = " (" + localversion + ")";
     261            }
     262            else
     263                localversion = "";
     264
     265            final JCheckBox pluginCheck = new JCheckBox(tr("{0}: Version {1}{2}", plugin.name, remoteversion, localversion), enabled);
     266            pluginPanel.add(pluginCheck);
     267
     268            pluginCheck.setToolTipText(plugin.resource != null ? ""+plugin.resource : tr("Plugin bundled with JOSM"));
     269            JLabel label = new JLabel("<html><i>"+(plugin.description==null?tr("no description available"):plugin.description)+"</i></html>");
     270            label.setBorder(BorderFactory.createEmptyBorder(0,20,0,0));
     271            label.setMaximumSize(new Dimension(width,1000));
     272            pluginPanel.add(label);
     273            pluginPanel.add(Box.createVerticalStrut(5));
     274
     275            pluginMap.put(plugin, enabled);
     276            pluginCheck.addActionListener(new ActionListener(){
     277                public void actionPerformed(ActionEvent e) {
    278278                    // if user enabled a plugin, it is not loaded but found somewhere on disk: offer to delete jar
    279279                    if (pluginCheck.isSelected()) {
     
    281281                        if ((PluginInformation.getLoaded(plugin.name) == null) && (plinfo != null)) {
    282282                            try {
    283                                 int answer = JOptionPane.showConfirmDialog(Main.parent, 
     283                                int answer = JOptionPane.showConfirmDialog(Main.parent,
    284284                                    tr("Plugin archive already available. Do you want to download current version by deleting existing archive?\n\n{0}",
    285285                                    plinfo.file.getCanonicalPath()), tr("Plugin already exists"), JOptionPane.OK_CANCEL_OPTION);
     
    295295                        }
    296296                    }
    297                                         pluginMap.put(plugin, pluginCheck.isSelected());
    298                                 }
    299                         });
    300                 }
    301                 plugin.updateUI();
    302         }
    303 
    304         private Collection<PluginDescription> getAvailablePlugins() {
    305                 SortedMap<String, PluginDescription> availablePlugins = new TreeMap<String, PluginDescription>(new Comparator<String>(){
    306                         public int compare(String o1, String o2) {
    307                                 return o1.compareToIgnoreCase(o2);
    308                         }
    309                 });
    310                 for (String location : PluginInformation.getPluginLocations()) {
    311                         File[] pluginFiles = new File(location).listFiles();
    312                         if (pluginFiles != null) {
    313                                 Arrays.sort(pluginFiles);
    314                                 for (File f : pluginFiles) {
    315                                         if (!f.isFile())
    316                                                 continue;
    317                                         if (f.getName().endsWith(".jar")) {
    318                                                 try {
    319                                                         PluginInformation info = new PluginInformation(f);
    320                                                         if (!availablePlugins.containsKey(info.name))
    321                                                                 availablePlugins.put(info.name, new PluginDescription(
    322                                                                         info.name,
    323                                                                         info.description,
    324                                                                         PluginInformation.fileToURL(f).toString(),
    325                                                                         info.version));
    326                                                 } catch (PluginException x) {
    327                                                 }
    328                                         } else if (f.getName().matches("^[0-9]+-site.*\\.xml$")) {
    329                                                 try {
    330                                                         Uniform<PluginDescription> parser = new Uniform<PluginDescription>(new FileReader(f), "plugin", PluginDescription.class);
    331                                                         for (PluginDescription pd : parser)
    332                                                                 if (!availablePlugins.containsKey(pd.name))
    333                                                                         availablePlugins.put(pd.name, pd);
    334                                                 } catch (Exception e) {
    335                                                         e.printStackTrace();
    336                                                         JOptionPane.showMessageDialog(Main.parent, tr("Error reading plugin information file: {0}", f.getName()));
    337                                                 }
    338                                         }
    339                                 }
    340                         }
    341                 }
    342                 for (PluginProxy proxy : Main.plugins)
    343                         if (!availablePlugins.containsKey(proxy.info.name))
    344                                 availablePlugins.put(proxy.info.name, new PluginDescription(
    345                                                 proxy.info.name,
    346                                                 proxy.info.description,
    347                                                 proxy.info.file == null ? null :
    348                                                         PluginInformation.fileToURL(proxy.info.file).toString(),
    349                                                 proxy.info.version));
    350                 return availablePlugins.values();
    351         }
    352 
    353         public void ok() {
    354                 Collection<PluginDescription> toDownload = new LinkedList<PluginDescription>();
    355                 String msg = "";
    356                 for (Entry<PluginDescription, Boolean> entry : pluginMap.entrySet()) {
    357                         if (entry.getValue() && PluginInformation.findPlugin(entry.getKey().name) == null) {
    358                                 toDownload.add(entry.getKey());
    359                                 msg += entry.getKey().name+"\n";
    360                         }
    361                 }
    362                 if (!toDownload.isEmpty()) {
    363                         int answer = JOptionPane.showConfirmDialog(Main.parent,
    364                                         tr("Download the following plugins?\n\n{0}", msg),
    365                                         tr("Download missing plugins"),
    366                                         JOptionPane.YES_NO_OPTION);
    367                         if (answer != JOptionPane.OK_OPTION)
    368                                 for (PluginDescription pd : toDownload)
    369                                         pluginMap.put(pd, false);
    370                         else
    371                                 for (PluginDescription pd : toDownload)
    372                                         if (!PluginDownloader.downloadPlugin(pd))
    373                                                 pluginMap.put(pd, false);
    374 
    375                 }
    376 
    377                 String oldPlugins = Main.pref.get("plugins");
    378                 LinkedList<String> plugins = new LinkedList<String>();
    379                 Object pd[] = pluginMap.keySet().toArray();
    380                 Arrays.sort(pd);
    381                 for (Object d : pd) {
    382                         if (pluginMap.get(d))
    383                                 plugins.add(((PluginDescription)d).name);
    384                 }
    385 
    386                 Main.pref.putCollection("plugins", plugins);
    387                 String newPlugins = Main.pref.get("plugins");
    388                 if(oldPlugins == null && plugins == null)
    389                         return;
    390                 if(plugins == null || oldPlugins == null || !plugins.equals(oldPlugins))
    391                         gui.requiresRestart = true;
    392         }
     297                    pluginMap.put(plugin, pluginCheck.isSelected());
     298                }
     299            });
     300        }
     301        plugin.updateUI();
     302    }
     303
     304    private Collection<PluginDescription> getAvailablePlugins() {
     305        SortedMap<String, PluginDescription> availablePlugins = new TreeMap<String, PluginDescription>(new Comparator<String>(){
     306            public int compare(String o1, String o2) {
     307                return o1.compareToIgnoreCase(o2);
     308            }
     309        });
     310        for (String location : PluginInformation.getPluginLocations()) {
     311            File[] pluginFiles = new File(location).listFiles();
     312            if (pluginFiles != null) {
     313                Arrays.sort(pluginFiles);
     314                for (File f : pluginFiles) {
     315                    if (!f.isFile())
     316                        continue;
     317                    if (f.getName().endsWith(".jar")) {
     318                        try {
     319                            PluginInformation info = new PluginInformation(f);
     320                            if (!availablePlugins.containsKey(info.name))
     321                                availablePlugins.put(info.name, new PluginDescription(
     322                                    info.name,
     323                                    info.description,
     324                                    PluginInformation.fileToURL(f).toString(),
     325                                    info.version));
     326                        } catch (PluginException x) {
     327                        }
     328                    } else if (f.getName().matches("^[0-9]+-site.*\\.xml$")) {
     329                        try {
     330                            Uniform<PluginDescription> parser = new Uniform<PluginDescription>(new FileReader(f), "plugin", PluginDescription.class);
     331                            for (PluginDescription pd : parser)
     332                                if (!availablePlugins.containsKey(pd.name))
     333                                    availablePlugins.put(pd.name, pd);
     334                        } catch (Exception e) {
     335                            e.printStackTrace();
     336                            JOptionPane.showMessageDialog(Main.parent, tr("Error reading plugin information file: {0}", f.getName()));
     337                        }
     338                    }
     339                }
     340            }
     341        }
     342        for (PluginProxy proxy : Main.plugins)
     343            if (!availablePlugins.containsKey(proxy.info.name))
     344                availablePlugins.put(proxy.info.name, new PluginDescription(
     345                        proxy.info.name,
     346                        proxy.info.description,
     347                        proxy.info.file == null ? null :
     348                            PluginInformation.fileToURL(proxy.info.file).toString(),
     349                        proxy.info.version));
     350        return availablePlugins.values();
     351    }
     352
     353    public void ok() {
     354        Collection<PluginDescription> toDownload = new LinkedList<PluginDescription>();
     355        String msg = "";
     356        for (Entry<PluginDescription, Boolean> entry : pluginMap.entrySet()) {
     357            if (entry.getValue() && PluginInformation.findPlugin(entry.getKey().name) == null) {
     358                toDownload.add(entry.getKey());
     359                msg += entry.getKey().name+"\n";
     360            }
     361        }
     362        if (!toDownload.isEmpty()) {
     363            int answer = JOptionPane.showConfirmDialog(Main.parent,
     364                    tr("Download the following plugins?\n\n{0}", msg),
     365                    tr("Download missing plugins"),
     366                    JOptionPane.YES_NO_OPTION);
     367            if (answer != JOptionPane.OK_OPTION)
     368                for (PluginDescription pd : toDownload)
     369                    pluginMap.put(pd, false);
     370            else
     371                for (PluginDescription pd : toDownload)
     372                    if (!PluginDownloader.downloadPlugin(pd))
     373                        pluginMap.put(pd, false);
     374
     375        }
     376
     377        String oldPlugins = Main.pref.get("plugins");
     378            LinkedList<String> plugins = new LinkedList<String>();
     379        Object pd[] = pluginMap.keySet().toArray();
     380        Arrays.sort(pd);
     381        for (Object d : pd) {
     382            if (pluginMap.get(d))
     383                plugins.add(((PluginDescription)d).name);
     384        }
     385
     386        Main.pref.putCollection("plugins", plugins);
     387        String newPlugins = Main.pref.get("plugins");
     388        if(oldPlugins == null && plugins == null)
     389            return;
     390        if(plugins == null || oldPlugins == null || !plugins.equals(oldPlugins))
     391            gui.requiresRestart = true;
     392    }
    393393}
  • trunk/src/org/openstreetmap/josm/gui/preferences/PreferenceDialog.java

    r1165 r1169  
    3434public class PreferenceDialog extends JTabbedPane {
    3535
    36         public final static Collection<PreferenceSetting> settings = new LinkedList<PreferenceSetting>();
     36    public final static Collection<PreferenceSetting> settings = new LinkedList<PreferenceSetting>();
    3737
    38         public boolean requiresRestart = false;
    39         public final RequireRestartAction requireRestartAction = new RequireRestartAction();
     38    public boolean requiresRestart = false;
     39    public final RequireRestartAction requireRestartAction = new RequireRestartAction();
    4040
    41         // some common tabs
    42         public final JPanel display = createPreferenceTab("display", tr("Display Settings"), tr("Various settings that influence the visual representation of the whole program."));
    43         public final JPanel connection = createPreferenceTab("connection", I18n.tr("Connection Settings"), I18n.tr("Connection Settings for the OSM server."));
    44         public final JPanel map = createPreferenceTab("map", I18n.tr("Map Settings"), I18n.tr("Settings for the map projection and data interpretation."));
    45         public final JPanel audio = createPreferenceTab("audio", I18n.tr("Audio Settings"), I18n.tr("Settings for the audio player and audio markers."));
     41    // some common tabs
     42    public final JPanel display = createPreferenceTab("display", tr("Display Settings"), tr("Various settings that influence the visual representation of the whole program."));
     43    public final JPanel connection = createPreferenceTab("connection", I18n.tr("Connection Settings"), I18n.tr("Connection Settings for the OSM server."));
     44    public final JPanel map = createPreferenceTab("map", I18n.tr("Map Settings"), I18n.tr("Settings for the map projection and data interpretation."));
     45    public final JPanel audio = createPreferenceTab("audio", I18n.tr("Audio Settings"), I18n.tr("Settings for the audio player and audio markers."));
    4646
    4747  public final javax.swing.JTabbedPane displaycontent = new javax.swing.JTabbedPane();
    4848
    49         /**
    50         * Construct a JPanel for the preference settings. Layout is GridBagLayout
    51          * and a centered title label and the description are added. The panel
    52          * will be shown inside a {@link ScrollPane}
    53         * @param icon The name of the icon.
    54         * @param title The title of this preference tab.
    55         * @param desc A description in one sentence for this tab. Will be displayed
    56          *              italic under the title.
    57         * @return The created panel ready to add other controls.
    58         */
    59         public JPanel createPreferenceTab(String icon, String title, String desc) {
    60                 return createPreferenceTab(icon, title, desc, true);
    61         }
     49    /**
     50    * Construct a JPanel for the preference settings. Layout is GridBagLayout
     51     * and a centered title label and the description are added. The panel
     52     * will be shown inside a {@link ScrollPane}
     53    * @param icon The name of the icon.
     54    * @param title The title of this preference tab.
     55    * @param desc A description in one sentence for this tab. Will be displayed
     56     *      italic under the title.
     57    * @return The created panel ready to add other controls.
     58    */
     59    public JPanel createPreferenceTab(String icon, String title, String desc) {
     60        return createPreferenceTab(icon, title, desc, true);
     61    }
    6262
    6363    /**
     
    6868     * @param desc A description in one sentence for this tab. Will be displayed
    6969     *      italic under the title.
    70      * @param inScrollPane if <code>true</code> the added tab will show scroll bars 
     70     * @param inScrollPane if <code>true</code> the added tab will show scroll bars
    7171     *        if the panel content is larger than the available space
    7272     * @return The created panel ready to add other controls.
     
    9393
    9494
    95         private final class RequireRestartAction implements ActionListener {
    96                 public void actionPerformed(ActionEvent e) {
    97                         requiresRestart = true;
    98                 }
    99         }
     95    private final class RequireRestartAction implements ActionListener {
     96        public void actionPerformed(ActionEvent e) {
     97            requiresRestart = true;
     98        }
     99    }
    100100
    101         public void ok() {
    102                 for (PreferenceSetting setting : settings)
    103                         setting.ok();
    104                 if (requiresRestart)
    105                         JOptionPane.showMessageDialog(Main.parent,tr("You have to restart JOSM for some settings to take effect."));
    106                 Main.parent.repaint();
    107         }
     101    public void ok() {
     102        for (PreferenceSetting setting : settings)
     103            setting.ok();
     104        if (requiresRestart)
     105            JOptionPane.showMessageDialog(Main.parent,tr("You have to restart JOSM for some settings to take effect."));
     106        Main.parent.repaint();
     107    }
    108108
    109         /**
    110         * If the dialog is closed with Ok, the preferences will be stored to the preferences-
    111         * file, otherwise no change of the file happens.
    112         */
    113         public PreferenceDialog() {
    114                 super(JTabbedPane.LEFT, JTabbedPane.SCROLL_TAB_LAYOUT);
    115                 display.add(displaycontent, GBC.eol().fill(GBC.BOTH));
    116                 for (Iterator<PreferenceSetting> it = settings.iterator(); it.hasNext();) {
    117                         try {
    118                     it.next().addGui(this);
     109    /**
     110    * If the dialog is closed with Ok, the preferences will be stored to the preferences-
     111    * file, otherwise no change of the file happens.
     112    */
     113    public PreferenceDialog() {
     114        super(JTabbedPane.LEFT, JTabbedPane.SCROLL_TAB_LAYOUT);
     115        display.add(displaycontent, GBC.eol().fill(GBC.BOTH));
     116        for (Iterator<PreferenceSetting> it = settings.iterator(); it.hasNext();) {
     117            try {
     118                it.next().addGui(this);
    119119            } catch (SecurityException e) {
    120                 it.remove();
     120                it.remove();
    121121            }
    122                 }
    123         }
     122        }
     123    }
    124124
    125         static {
    126                 // order is important!
    127                 settings.add(new DrawingPreference());
    128                 settings.add(new ColorPreference());
    129                 settings.add(new LafPreference());
    130                 settings.add(new LanguagePreference());
    131                 settings.add(new MapPaintPreference());
    132                 settings.add(new ServerAccessPreference());
    133                 settings.add(new FilePreferences());
    134                 settings.add(new ProxyPreferences());
    135                 settings.add(new ProjectionPreference());
    136                 settings.add(new TaggingPresetPreference());
    137                 settings.add(new PluginPreference());
    138                 settings.add(Main.toolbar);
    139                 settings.add(new AudioPreference());
    140                 settings.add(new ShortcutPreference());
     125    static {
     126        // order is important!
     127        settings.add(new DrawingPreference());
     128        settings.add(new ColorPreference());
     129        settings.add(new LafPreference());
     130        settings.add(new LanguagePreference());
     131        settings.add(new MapPaintPreference());
     132        settings.add(new ServerAccessPreference());
     133        settings.add(new FilePreferences());
     134        settings.add(new ProxyPreferences());
     135        settings.add(new ProjectionPreference());
     136        settings.add(new TaggingPresetPreference());
     137        settings.add(new PluginPreference());
     138        settings.add(Main.toolbar);
     139        settings.add(new AudioPreference());
     140        settings.add(new ShortcutPreference());
    141141
    142                 for (PluginProxy plugin : Main.plugins) {
    143                         PreferenceSetting p = plugin.getPreferenceSetting();
    144                         if (p != null)
    145                                 settings.add(p);
    146                 }
     142        for (PluginProxy plugin : Main.plugins) {
     143            PreferenceSetting p = plugin.getPreferenceSetting();
     144            if (p != null)
     145                settings.add(p);
     146        }
    147147
    148                 // always the last: advanced tab
    149                 settings.add(new AdvancedPreference());
    150         }
     148        // always the last: advanced tab
     149        settings.add(new AdvancedPreference());
     150    }
    151151}
  • trunk/src/org/openstreetmap/josm/gui/preferences/PreferenceSetting.java

    r999 r1169  
    44
    55public interface PreferenceSetting {
    6         /**
    7         * Add the GUI elements to the dialog. The elements should be initialized after
    8         * the current preferences.
    9         */
    10         void addGui(PreferenceDialog gui);
     6    /**
     7    * Add the GUI elements to the dialog. The elements should be initialized after
     8    * the current preferences.
     9    */
     10    void addGui(PreferenceDialog gui);
    1111
    12         /**
    13         * Called when OK is pressed to save the setting in the preferences file.
    14         */
    15         void ok();
     12    /**
     13    * Called when OK is pressed to save the setting in the preferences file.
     14    */
     15    void ok();
    1616}
  • trunk/src/org/openstreetmap/josm/gui/preferences/ProjectionPreference.java

    r1108 r1169  
    1919public class ProjectionPreference implements PreferenceSetting {
    2020
    21         /**
    22         * Combobox with all projections available
    23         */
    24         private JComboBox projectionCombo = new JComboBox(Projection.allProjections);
    25         private JComboBox coordinatesCombo = new JComboBox(CoordinateFormat.values());
     21    /**
     22    * Combobox with all projections available
     23    */
     24    private JComboBox projectionCombo = new JComboBox(Projection.allProjections);
     25    private JComboBox coordinatesCombo = new JComboBox(CoordinateFormat.values());
    2626
    27         public void addGui(PreferenceDialog gui) {
    28                
    29             for (int i = 0; i < projectionCombo.getItemCount(); ++i) {
    30                 if (projectionCombo.getItemAt(i).getClass().getName().equals(Main.pref.get("projection"))) {
    31                     projectionCombo.setSelectedIndex(i);
    32                     break;
    33                 }
    34             }
     27    public void addGui(PreferenceDialog gui) {
    3528
    36             for (int i = 0; i < coordinatesCombo.getItemCount(); ++i) {
    37                 if (((CoordinateFormat)coordinatesCombo.getItemAt(i)).name().equals(Main.pref.get("coordinates"))) {
    38                     coordinatesCombo.setSelectedIndex(i);
    39                     break;
    40                 }
    41             }
     29        for (int i = 0; i < projectionCombo.getItemCount(); ++i) {
     30            if (projectionCombo.getItemAt(i).getClass().getName().equals(Main.pref.get("projection"))) {
     31                projectionCombo.setSelectedIndex(i);
     32                break;
     33            }
     34        }
    4235
    43                 projectionCombo.addActionListener(gui.requireRestartAction);
     36        for (int i = 0; i < coordinatesCombo.getItemCount(); ++i) {
     37            if (((CoordinateFormat)coordinatesCombo.getItemAt(i)).name().equals(Main.pref.get("coordinates"))) {
     38                coordinatesCombo.setSelectedIndex(i);
     39                break;
     40            }
     41        }
     42
     43        projectionCombo.addActionListener(gui.requireRestartAction);
    4444        coordinatesCombo.addActionListener(gui.requireRestartAction);
    45                
    46                 JPanel projPanel = new JPanel();
    47                 projPanel.setBorder(BorderFactory.createTitledBorder(BorderFactory.createLineBorder(Color.gray), tr("Map Projection")));
    48                 projPanel.setLayout(new GridBagLayout());
    49                 projPanel.add(new JLabel(tr("Projection method")), GBC.std().insets(5,5,0,5));
    50                 projPanel.add(GBC.glue(5,0), GBC.std().fill(GBC.HORIZONTAL));
    51                 projPanel.add(projectionCombo, GBC.eop().fill(GBC.HORIZONTAL).insets(0,5,5,5));
    52             projPanel.add(new JLabel(tr("Display coordinates as")), GBC.std().insets(5,5,0,5));
    53             projPanel.add(GBC.glue(5,0), GBC.std().fill(GBC.HORIZONTAL));
    54             projPanel.add(coordinatesCombo, GBC.eop().fill(GBC.HORIZONTAL).insets(0,5,5,5));
    55                 gui.map.add(projPanel, GBC.eol().insets(0,0,0,10).fill(GBC.HORIZONTAL));
     45
     46        JPanel projPanel = new JPanel();
     47        projPanel.setBorder(BorderFactory.createTitledBorder(BorderFactory.createLineBorder(Color.gray), tr("Map Projection")));
     48        projPanel.setLayout(new GridBagLayout());
     49        projPanel.add(new JLabel(tr("Projection method")), GBC.std().insets(5,5,0,5));
     50        projPanel.add(GBC.glue(5,0), GBC.std().fill(GBC.HORIZONTAL));
     51        projPanel.add(projectionCombo, GBC.eop().fill(GBC.HORIZONTAL).insets(0,5,5,5));
     52        projPanel.add(new JLabel(tr("Display coordinates as")), GBC.std().insets(5,5,0,5));
     53        projPanel.add(GBC.glue(5,0), GBC.std().fill(GBC.HORIZONTAL));
     54        projPanel.add(coordinatesCombo, GBC.eop().fill(GBC.HORIZONTAL).insets(0,5,5,5));
     55        gui.map.add(projPanel, GBC.eol().insets(0,0,0,10).fill(GBC.HORIZONTAL));
    5656    }
    5757
    58         public void ok() {
    59                 Main.pref.put("projection", projectionCombo.getSelectedItem().getClass().getName());
    60                 Main.pref.put("coordinates", ((CoordinateFormat)coordinatesCombo.getSelectedItem()).name());
     58    public void ok() {
     59        Main.pref.put("projection", projectionCombo.getSelectedItem().getClass().getName());
     60        Main.pref.put("coordinates", ((CoordinateFormat)coordinatesCombo.getSelectedItem()).name());
    6161    }
    6262}
  • trunk/src/org/openstreetmap/josm/gui/preferences/ProxyPreferences.java

    r1053 r1169  
    2020public class ProxyPreferences implements PreferenceSetting {
    2121
    22         public static final String PROXY_ENABLE = "proxy.enable";
    23         public static final String PROXY_HOST = "proxy.host";
    24         public static final String PROXY_PORT = "proxy.port";
    25         public static final String PROXY_ANONYMOUS = "proxy.anonymous";
    26         public static final String PROXY_USER = "proxy.user";
    27         public static final String PROXY_PASS = "proxy.pass";
     22    public static final String PROXY_ENABLE = "proxy.enable";
     23    public static final String PROXY_HOST = "proxy.host";
     24    public static final String PROXY_PORT = "proxy.port";
     25    public static final String PROXY_ANONYMOUS = "proxy.anonymous";
     26    public static final String PROXY_USER = "proxy.user";
     27    public static final String PROXY_PASS = "proxy.pass";
    2828
    29         private JCheckBox proxyEnable = new JCheckBox(tr("Enable proxy server"));
    30         private JTextField proxyHost = new JTextField(20);
    31         private JTextField proxyPort = new JTextField(5);
    32         private JCheckBox proxyAnonymous = new JCheckBox(tr("Anonymous"));
    33         private JTextField proxyUser = new JTextField(20);
    34         private JPasswordField proxyPass = new JPasswordField(20);
     29    private JCheckBox proxyEnable = new JCheckBox(tr("Enable proxy server"));
     30    private JTextField proxyHost = new JTextField(20);
     31    private JTextField proxyPort = new JTextField(5);
     32    private JCheckBox proxyAnonymous = new JCheckBox(tr("Anonymous"));
     33    private JTextField proxyUser = new JTextField(20);
     34    private JPasswordField proxyPass = new JPasswordField(20);
    3535
    36         public void addGui(PreferenceDialog gui) {
    37                 proxyEnable.setSelected(Main.pref.getBoolean(PROXY_ENABLE));
    38                 proxyEnable.addActionListener(new ActionListener(){
    39                         public void actionPerformed(ActionEvent e) {
    40                                 proxyHost.setEnabled(proxyEnable.isSelected());
    41                                 proxyPort.setEnabled(proxyEnable.isSelected());
    42                                 proxyAnonymous.setEnabled(proxyEnable.isSelected());
    43                                 proxyUser.setEnabled(proxyEnable.isSelected() && !proxyAnonymous.isSelected());
    44                                 proxyPass.setEnabled(proxyEnable.isSelected() && !proxyAnonymous.isSelected());
    45                         }
    46                 });
    47                 proxyHost.setEnabled(Main.pref.getBoolean(PROXY_ENABLE));
    48                 proxyHost.setText(Main.pref.get(PROXY_HOST));
    49                 proxyPort.setEnabled(Main.pref.getBoolean(PROXY_ENABLE));
    50                 proxyPort.setText(Main.pref.get(PROXY_PORT));
    51                 proxyAnonymous.setEnabled(Main.pref.getBoolean(PROXY_ENABLE));
    52                 proxyAnonymous.setSelected(Main.pref.getBoolean(PROXY_ANONYMOUS));
    53                 proxyAnonymous.addActionListener(new ActionListener(){
    54                         public void actionPerformed(ActionEvent e) {
    55                                 proxyUser.setEnabled(proxyEnable.isSelected() && !proxyAnonymous.isSelected());
    56                                 proxyPass.setEnabled(proxyEnable.isSelected() && !proxyAnonymous.isSelected());
    57                         }
    58                 });
    59                 proxyUser.setEnabled(Main.pref.getBoolean(PROXY_ENABLE) && (Main.pref.getBoolean(PROXY_ANONYMOUS)));
    60                 proxyUser.setText(Main.pref.get(PROXY_USER));
    61                 proxyPass.setEnabled(Main.pref.getBoolean(PROXY_ENABLE) && (Main.pref.getBoolean(PROXY_ANONYMOUS)));
    62                 proxyPass.setText(Main.pref.get(PROXY_USER));
    63                
    64                 gui.connection.add(new JSeparator(SwingConstants.HORIZONTAL), GBC.eol().fill(GBC.HORIZONTAL));
    65                 gui.connection.add(new JLabel(tr("Proxy Settings")), GBC.eol());
    66                 gui.connection.add(proxyEnable, GBC.eol().insets(20, 0, 0, 0));
    67                 gui.connection.add(new JLabel(tr("Proxy server host")), GBC.std());
    68                 gui.connection.add(proxyHost, GBC.eol().fill(GBC.HORIZONTAL).insets(5,0,0,5));
    69                 gui.connection.add(new JLabel(tr("Proxy server port")), GBC.std());
    70                 gui.connection.add(proxyPort, GBC.eol().fill(GBC.HORIZONTAL).insets(5,0,0,5));
    71                 gui.connection.add(proxyAnonymous, GBC.eop().insets(20, 0, 0, 0));
    72                 gui.connection.add(new JLabel(tr("Proxy server username")), GBC.std());
    73                 gui.connection.add(proxyUser, GBC.eol().fill(GBC.HORIZONTAL).insets(5,0,0,5));
    74                 gui.connection.add(new JLabel(tr("Proxy server password")), GBC.std());
    75                 gui.connection.add(proxyPass, GBC.eol().fill(GBC.HORIZONTAL).insets(5,0,0,5));
     36    public void addGui(PreferenceDialog gui) {
     37        proxyEnable.setSelected(Main.pref.getBoolean(PROXY_ENABLE));
     38        proxyEnable.addActionListener(new ActionListener(){
     39            public void actionPerformed(ActionEvent e) {
     40                proxyHost.setEnabled(proxyEnable.isSelected());
     41                proxyPort.setEnabled(proxyEnable.isSelected());
     42                proxyAnonymous.setEnabled(proxyEnable.isSelected());
     43                proxyUser.setEnabled(proxyEnable.isSelected() && !proxyAnonymous.isSelected());
     44                proxyPass.setEnabled(proxyEnable.isSelected() && !proxyAnonymous.isSelected());
     45            }
     46        });
     47        proxyHost.setEnabled(Main.pref.getBoolean(PROXY_ENABLE));
     48        proxyHost.setText(Main.pref.get(PROXY_HOST));
     49        proxyPort.setEnabled(Main.pref.getBoolean(PROXY_ENABLE));
     50        proxyPort.setText(Main.pref.get(PROXY_PORT));
     51        proxyAnonymous.setEnabled(Main.pref.getBoolean(PROXY_ENABLE));
     52        proxyAnonymous.setSelected(Main.pref.getBoolean(PROXY_ANONYMOUS));
     53        proxyAnonymous.addActionListener(new ActionListener(){
     54            public void actionPerformed(ActionEvent e) {
     55                proxyUser.setEnabled(proxyEnable.isSelected() && !proxyAnonymous.isSelected());
     56                proxyPass.setEnabled(proxyEnable.isSelected() && !proxyAnonymous.isSelected());
     57            }
     58        });
     59        proxyUser.setEnabled(Main.pref.getBoolean(PROXY_ENABLE) && (Main.pref.getBoolean(PROXY_ANONYMOUS)));
     60        proxyUser.setText(Main.pref.get(PROXY_USER));
     61        proxyPass.setEnabled(Main.pref.getBoolean(PROXY_ENABLE) && (Main.pref.getBoolean(PROXY_ANONYMOUS)));
     62        proxyPass.setText(Main.pref.get(PROXY_USER));
    7663
    77                 gui.connection.add(Box.createVerticalGlue(), GBC.eol().fill(GBC.VERTICAL));
    78         }
     64        gui.connection.add(new JSeparator(SwingConstants.HORIZONTAL), GBC.eol().fill(GBC.HORIZONTAL));
     65        gui.connection.add(new JLabel(tr("Proxy Settings")), GBC.eol());
     66        gui.connection.add(proxyEnable, GBC.eol().insets(20, 0, 0, 0));
     67        gui.connection.add(new JLabel(tr("Proxy server host")), GBC.std());
     68        gui.connection.add(proxyHost, GBC.eol().fill(GBC.HORIZONTAL).insets(5,0,0,5));
     69        gui.connection.add(new JLabel(tr("Proxy server port")), GBC.std());
     70        gui.connection.add(proxyPort, GBC.eol().fill(GBC.HORIZONTAL).insets(5,0,0,5));
     71        gui.connection.add(proxyAnonymous, GBC.eop().insets(20, 0, 0, 0));
     72        gui.connection.add(new JLabel(tr("Proxy server username")), GBC.std());
     73        gui.connection.add(proxyUser, GBC.eol().fill(GBC.HORIZONTAL).insets(5,0,0,5));
     74        gui.connection.add(new JLabel(tr("Proxy server password")), GBC.std());
     75        gui.connection.add(proxyPass, GBC.eol().fill(GBC.HORIZONTAL).insets(5,0,0,5));
    7976
    80         public void ok() {
    81                 Main.pref.put(PROXY_ENABLE, proxyEnable.isSelected());
    82                 Main.pref.put(PROXY_HOST, proxyHost.getText());
    83                 Main.pref.put(PROXY_PORT, proxyPort.getText());
    84                 Main.pref.put(PROXY_ANONYMOUS, proxyAnonymous.isSelected());
    85                 Main.pref.put(PROXY_USER, proxyUser.getText());
    86                 Main.pref.put(PROXY_PASS, new String(proxyPass.getPassword()));
    87         }
     77        gui.connection.add(Box.createVerticalGlue(), GBC.eol().fill(GBC.VERTICAL));
     78    }
     79
     80    public void ok() {
     81        Main.pref.put(PROXY_ENABLE, proxyEnable.isSelected());
     82        Main.pref.put(PROXY_HOST, proxyHost.getText());
     83        Main.pref.put(PROXY_PORT, proxyPort.getText());
     84        Main.pref.put(PROXY_ANONYMOUS, proxyAnonymous.isSelected());
     85        Main.pref.put(PROXY_USER, proxyUser.getText());
     86        Main.pref.put(PROXY_PASS, new String(proxyPass.getPassword()));
     87    }
    8888
    8989}
  • trunk/src/org/openstreetmap/josm/gui/preferences/ServerAccessPreference.java

    r627 r1169  
    1515public class ServerAccessPreference implements PreferenceSetting {
    1616
    17         /**
    18         * Editfield for the Base url to the REST API from OSM.
    19         */
    20         private JTextField osmDataServer = new JTextField(20);
    21         /**
    22         * Editfield for the username to the OSM account.
    23         */
    24         private JTextField osmDataUsername = new JTextField(20);
    25         /**
    26         * Passwordfield for the userpassword of the REST API.
    27         */
    28         private JPasswordField osmDataPassword = new JPasswordField(20);
     17    /**
     18    * Editfield for the Base url to the REST API from OSM.
     19    */
     20    private JTextField osmDataServer = new JTextField(20);
     21    /**
     22    * Editfield for the username to the OSM account.
     23    */
     24    private JTextField osmDataUsername = new JTextField(20);
     25    /**
     26    * Passwordfield for the userpassword of the REST API.
     27    */
     28    private JPasswordField osmDataPassword = new JPasswordField(20);
    2929
    30         public void addGui(PreferenceDialog gui) {
    31                 osmDataServer.setText(Main.pref.get("osm-server.url"));
    32                 osmDataUsername.setText(Main.pref.get("osm-server.username"));
    33                 osmDataPassword.setText(Main.pref.get("osm-server.password"));
     30    public void addGui(PreferenceDialog gui) {
     31        osmDataServer.setText(Main.pref.get("osm-server.url"));
     32        osmDataUsername.setText(Main.pref.get("osm-server.username"));
     33        osmDataPassword.setText(Main.pref.get("osm-server.password"));
    3434
    35                 osmDataServer.setToolTipText(tr("The base URL for the OSM server (REST API)"));
    36                 osmDataUsername.setToolTipText(tr("Login name (email) to the OSM account."));
    37                 osmDataPassword.setToolTipText(tr("Login password to the OSM account. Leave blank to not store any password."));
     35        osmDataServer.setToolTipText(tr("The base URL for the OSM server (REST API)"));
     36        osmDataUsername.setToolTipText(tr("Login name (email) to the OSM account."));
     37        osmDataPassword.setToolTipText(tr("Login password to the OSM account. Leave blank to not store any password."));
    3838
    39                 gui.connection.add(new JLabel(tr("Base Server URL")), GBC.std());
    40                 gui.connection.add(osmDataServer, GBC.eol().fill(GBC.HORIZONTAL).insets(5,0,0,5));
    41                 gui.connection.add(new JLabel(tr("OSM username (email)")), GBC.std());
    42                 gui.connection.add(osmDataUsername, GBC.eol().fill(GBC.HORIZONTAL).insets(5,0,0,5));
    43                 gui.connection.add(new JLabel(tr("OSM password")), GBC.std());
    44                 gui.connection.add(osmDataPassword, GBC.eol().fill(GBC.HORIZONTAL).insets(5,0,0,0));
    45                 JLabel warning = new JLabel(tr("<html>" +
    46                                 "WARNING: The password is stored in plain text in the preferences file.<br>" +
    47                                 "The password is transfered in plain text to the server, encoded in the url.<br>" +
    48                 "<b>Do not use a valuable Password.</b></html>"));
    49                 warning.setFont(warning.getFont().deriveFont(Font.ITALIC));
    50                 gui.connection.add(warning, GBC.eop().fill(GBC.HORIZONTAL));
    51         }
     39        gui.connection.add(new JLabel(tr("Base Server URL")), GBC.std());
     40        gui.connection.add(osmDataServer, GBC.eol().fill(GBC.HORIZONTAL).insets(5,0,0,5));
     41        gui.connection.add(new JLabel(tr("OSM username (email)")), GBC.std());
     42        gui.connection.add(osmDataUsername, GBC.eol().fill(GBC.HORIZONTAL).insets(5,0,0,5));
     43        gui.connection.add(new JLabel(tr("OSM password")), GBC.std());
     44        gui.connection.add(osmDataPassword, GBC.eol().fill(GBC.HORIZONTAL).insets(5,0,0,0));
     45        JLabel warning = new JLabel(tr("<html>" +
     46                "WARNING: The password is stored in plain text in the preferences file.<br>" +
     47                "The password is transfered in plain text to the server, encoded in the url.<br>" +
     48        "<b>Do not use a valuable Password.</b></html>"));
     49        warning.setFont(warning.getFont().deriveFont(Font.ITALIC));
     50        gui.connection.add(warning, GBC.eop().fill(GBC.HORIZONTAL));
     51    }
    5252
    53         public void ok() {
    54                 Main.pref.put("osm-server.url", osmDataServer.getText());
    55                 Main.pref.put("osm-server.username", osmDataUsername.getText());
    56                 String pwd = String.valueOf(osmDataPassword.getPassword());
    57                 if (pwd.equals(""))
    58                         pwd = null;
    59                 Main.pref.put("osm-server.password", pwd);
     53    public void ok() {
     54        Main.pref.put("osm-server.url", osmDataServer.getText());
     55        Main.pref.put("osm-server.username", osmDataUsername.getText());
     56        String pwd = String.valueOf(osmDataPassword.getPassword());
     57        if (pwd.equals(""))
     58            pwd = null;
     59        Main.pref.put("osm-server.password", pwd);
    6060    }
    6161}
  • trunk/src/org/openstreetmap/josm/gui/preferences/ShortcutPreference.java

    r1084 r1169  
    1515public class ShortcutPreference implements PreferenceSetting {
    1616
    17         public void addGui(PreferenceDialog gui) {
    18                 // icon source: http://www.iconfinder.net/index.php?q=key&page=icondetails&iconid=8553&size=128&q=key&s12=on&s16=on&s22=on&s32=on&s48=on&s64=on&s128=on
    19                 // icon licence: GPL
    20                 // icon designer: Paolino, http://www.paolinoland.it/
    21                 // icon original filename: keyboard.png
    22                 // icon original size: 128x128
    23                 // modifications: icon was cropped, then resized
    24                 JPanel p = gui.createPreferenceTab("shortcuts", tr("Shortcut Preferences"),
    25                         tr("Changing keyboard shortcuts manually."), false);
     17    public void addGui(PreferenceDialog gui) {
     18        // icon source: http://www.iconfinder.net/index.php?q=key&page=icondetails&iconid=8553&size=128&q=key&s12=on&s16=on&s22=on&s32=on&s48=on&s64=on&s128=on
     19        // icon licence: GPL
     20        // icon designer: Paolino, http://www.paolinoland.it/
     21        // icon original filename: keyboard.png
     22        // icon original size: 128x128
     23        // modifications: icon was cropped, then resized
     24        JPanel p = gui.createPreferenceTab("shortcuts", tr("Shortcut Preferences"),
     25                tr("Changing keyboard shortcuts manually."), false);
    2626
    27                 prefJPanel prefpanel = new prefJPanel(new scListModel());
    28                 p.add(prefpanel, GBC.eol().fill(GBC.BOTH));
     27        prefJPanel prefpanel = new prefJPanel(new scListModel());
     28        p.add(prefpanel, GBC.eol().fill(GBC.BOTH));
    2929
    30         }
     30    }
    3131
    32         public void ok() {
     32    public void ok() {
    3333   }
    3434
    35         // Maybe move this to prefPanel? There's no need for it to be here.
    36         private class scListModel extends AbstractTableModel {
    37 //              private String[] columnNames = new String[]{tr("Action"), tr("Shortcut"), tr("Group"), tr("ID")};
    38                 private String[] columnNames = new String[]{tr("Action"), tr("Shortcut")};
    39                 private Collection<Shortcut> data;
    40                 public scListModel() {
    41                         data = Shortcut.listAll();
    42                 }
    43                 public int getColumnCount() {
    44                         return columnNames.length;
    45                 }
    46                 public int getRowCount() {
    47                         return data.size();
    48                 }
    49                 public String getColumnName(int col) {
    50                         return columnNames[col];
    51                 }
    52                 public Object getValueAt(int row, int col) {
    53                         Shortcut sc = (Shortcut)data.toArray()[row];
    54                         if (col == 0) {
    55                                 return sc.getLongText();
    56                         } else if (col == 1) {
    57                                 return sc.getKeyText();
    58                         } /*else if (col == 2) {
    59                                 if (sc.getRequestedGroup() == Shortcut.GROUP_NONE) {
    60                                         return tr("None");
    61                                 } else if (sc.getRequestedGroup() == Shortcut.GROUP_HOTKEY) {
    62                                         return tr("Hotkey");
    63                                 } else if (sc.getRequestedGroup() == Shortcut.GROUP_MENU) {
    64                                         return tr("Menu");
    65                                 } else if (sc.getRequestedGroup() == Shortcut.GROUP_EDIT) {
    66                                         return tr("Edit");
    67                                 } else if (sc.getRequestedGroup() == Shortcut.GROUP_LAYER) {
    68                                         return tr("Subwindow");
    69                                 } else if (sc.getRequestedGroup() == Shortcut.GROUP_DIRECT) {
    70                                         return tr("Direct");
    71                                 } else if (sc.getRequestedGroup() == Shortcut.GROUP_MNEMONIC) {
    72                                         return tr("Mnemonic");
    73                                 } else if (sc.getRequestedGroup() == Shortcut.GROUP_RESERVED) {
    74                                         return tr("System");
    75                                 } else {
    76                                         return tr("Unknown");
    77                                 }
    78                         } else if (col == 3) {
    79                                 return sc.getShortText();
    80                         } */else {
    81                                 // This is a kind of hack that allows the actions on the editing controls
    82                                 // to access the underlying shortcut object without introducing another
    83                                 // method. I opted to stay within the interface.
    84                                 return sc;
    85                         }
    86                 }
    87                 public boolean isCellEditable(int row, int col) {
    88                         return false;
    89                 }
    90         }
     35    // Maybe move this to prefPanel? There's no need for it to be here.
     36    private class scListModel extends AbstractTableModel {
     37//      private String[] columnNames = new String[]{tr("Action"), tr("Shortcut"), tr("Group"), tr("ID")};
     38        private String[] columnNames = new String[]{tr("Action"), tr("Shortcut")};
     39        private Collection<Shortcut> data;
     40        public scListModel() {
     41            data = Shortcut.listAll();
     42        }
     43        public int getColumnCount() {
     44            return columnNames.length;
     45        }
     46        public int getRowCount() {
     47            return data.size();
     48        }
     49        public String getColumnName(int col) {
     50            return columnNames[col];
     51        }
     52        public Object getValueAt(int row, int col) {
     53            Shortcut sc = (Shortcut)data.toArray()[row];
     54            if (col == 0) {
     55                return sc.getLongText();
     56            } else if (col == 1) {
     57                return sc.getKeyText();
     58            } /*else if (col == 2) {
     59                if (sc.getRequestedGroup() == Shortcut.GROUP_NONE) {
     60                    return tr("None");
     61                } else if (sc.getRequestedGroup() == Shortcut.GROUP_HOTKEY) {
     62                    return tr("Hotkey");
     63                } else if (sc.getRequestedGroup() == Shortcut.GROUP_MENU) {
     64                    return tr("Menu");
     65                } else if (sc.getRequestedGroup() == Shortcut.GROUP_EDIT) {
     66                    return tr("Edit");
     67                } else if (sc.getRequestedGroup() == Shortcut.GROUP_LAYER) {
     68                    return tr("Subwindow");
     69                } else if (sc.getRequestedGroup() == Shortcut.GROUP_DIRECT) {
     70                    return tr("Direct");
     71                } else if (sc.getRequestedGroup() == Shortcut.GROUP_MNEMONIC) {
     72                    return tr("Mnemonic");
     73                } else if (sc.getRequestedGroup() == Shortcut.GROUP_RESERVED) {
     74                    return tr("System");
     75                } else {
     76                    return tr("Unknown");
     77                }
     78            } else if (col == 3) {
     79                return sc.getShortText();
     80            } */else {
     81                // This is a kind of hack that allows the actions on the editing controls
     82                // to access the underlying shortcut object without introducing another
     83                // method. I opted to stay within the interface.
     84                return sc;
     85            }
     86        }
     87        public boolean isCellEditable(int row, int col) {
     88            return false;
     89        }
     90    }
    9191
    9292}
  • trunk/src/org/openstreetmap/josm/gui/preferences/TaggingPresetPreference.java

    r999 r1169  
    3434public class TaggingPresetPreference implements PreferenceSetting {
    3535
    36         public static Collection<TaggingPreset> taggingPresets;
    37         private JList taggingPresetSources;
    38         private JCheckBox enableDefault;
    39        
    40         public void addGui(final PreferenceDialog gui) {
    41                
    42                 taggingPresetSources = new JList(new DefaultListModel());
    43                 enableDefault = new JCheckBox(tr("Enable built-in defaults"),
    44                                 Main.pref.getBoolean("taggingpreset.enable-defaults", true));
     36    public static Collection<TaggingPreset> taggingPresets;
     37    private JList taggingPresetSources;
     38    private JCheckBox enableDefault;
    4539
    46                 String annos = Main.pref.get("taggingpreset.sources");
    47                 StringTokenizer st = new StringTokenizer(annos, ";");
    48                 while (st.hasMoreTokens())
    49                         ((DefaultListModel)taggingPresetSources.getModel()).addElement(st.nextToken());
     40    public void addGui(final PreferenceDialog gui) {
    5041
    51                 JButton addAnno = new JButton(tr("Add"));
    52                 addAnno.addActionListener(new ActionListener(){
    53                         public void actionPerformed(ActionEvent e) {
    54                                 String source = JOptionPane.showInputDialog(Main.parent, tr("Tagging preset source"));
    55                                 if (source == null)
    56                                         return;
    57                                 ((DefaultListModel)taggingPresetSources.getModel()).addElement(source);
    58                                 gui.requiresRestart = true;
    59                         }
    60                 });
     42        taggingPresetSources = new JList(new DefaultListModel());
     43        enableDefault = new JCheckBox(tr("Enable built-in defaults"),
     44                Main.pref.getBoolean("taggingpreset.enable-defaults", true));
    6145
    62                 JButton editAnno = new JButton(tr("Edit"));
    63                 editAnno.addActionListener(new ActionListener(){
    64                         public void actionPerformed(ActionEvent e) {
    65                                 if (taggingPresetSources.getSelectedIndex() == -1)
    66                                         JOptionPane.showMessageDialog(Main.parent, tr("Please select the row to edit."));
    67                                 else {
    68                                         String source = JOptionPane.showInputDialog(Main.parent, tr("Tagging preset source"), taggingPresetSources.getSelectedValue());
    69                                         if (source == null)
    70                                                 return;
    71                                         ((DefaultListModel)taggingPresetSources.getModel()).setElementAt(source, taggingPresetSources.getSelectedIndex());
    72                                         gui.requiresRestart = true;
    73                                 }
    74                         }
    75                 });
     46        String annos = Main.pref.get("taggingpreset.sources");
     47        StringTokenizer st = new StringTokenizer(annos, ";");
     48        while (st.hasMoreTokens())
     49            ((DefaultListModel)taggingPresetSources.getModel()).addElement(st.nextToken());
    7650
    77                 JButton deleteAnno = new JButton(tr("Delete"));
    78                 deleteAnno.addActionListener(new ActionListener(){
    79                         public void actionPerformed(ActionEvent e) {
    80                                 if (taggingPresetSources.getSelectedIndex() == -1)
    81                                         JOptionPane.showMessageDialog(Main.parent, tr("Please select the row to delete."));
    82                                 else {
    83                                         ((DefaultListModel)taggingPresetSources.getModel()).remove(taggingPresetSources.getSelectedIndex());
    84                                         gui.requiresRestart = true;
    85                                 }
    86                         }
    87                 });
    88                 taggingPresetSources.setVisibleRowCount(3);
     51        JButton addAnno = new JButton(tr("Add"));
     52        addAnno.addActionListener(new ActionListener(){
     53            public void actionPerformed(ActionEvent e) {
     54                String source = JOptionPane.showInputDialog(Main.parent, tr("Tagging preset source"));
     55                if (source == null)
     56                    return;
     57                ((DefaultListModel)taggingPresetSources.getModel()).addElement(source);
     58                gui.requiresRestart = true;
     59            }
     60        });
    8961
    90                 taggingPresetSources.setToolTipText(tr("The sources (url or filename) of tagging preset definition files. See http://josm.openstreetmap.de/wiki/TaggingPresets for help."));
    91                 addAnno.setToolTipText(tr("Add a new tagging preset source to the list."));
    92                 deleteAnno.setToolTipText(tr("Delete the selected source from the list."));
     62        JButton editAnno = new JButton(tr("Edit"));
     63        editAnno.addActionListener(new ActionListener(){
     64            public void actionPerformed(ActionEvent e) {
     65                if (taggingPresetSources.getSelectedIndex() == -1)
     66                    JOptionPane.showMessageDialog(Main.parent, tr("Please select the row to edit."));
     67                else {
     68                    String source = JOptionPane.showInputDialog(Main.parent, tr("Tagging preset source"), taggingPresetSources.getSelectedValue());
     69                    if (source == null)
     70                        return;
     71                    ((DefaultListModel)taggingPresetSources.getModel()).setElementAt(source, taggingPresetSources.getSelectedIndex());
     72                    gui.requiresRestart = true;
     73                }
     74            }
     75        });
    9376
    94                 JPanel tpPanel = new JPanel();
    95                 tpPanel.setBorder(BorderFactory.createTitledBorder(BorderFactory.createLineBorder(Color.gray), tr("Tagging Presets")));
    96                 tpPanel.setLayout(new GridBagLayout());
    97                 tpPanel.add(enableDefault, GBC.eol().insets(5,5,5,0));
    98                 tpPanel.add(new JLabel(tr("Tagging preset sources")), GBC.eol().insets(5,5,5,0));
    99                 tpPanel.add(new JScrollPane(taggingPresetSources), GBC.eol().insets(5,0,5,0).fill(GBC.BOTH));
    100                 JPanel buttonPanel = new JPanel(new GridBagLayout());
    101                 tpPanel.add(buttonPanel, GBC.eol().insets(5,0,5,5).fill(GBC.HORIZONTAL));
    102                 buttonPanel.add(Box.createHorizontalGlue(), GBC.std().fill(GBC.HORIZONTAL));
    103                 buttonPanel.add(addAnno, GBC.std().insets(0,5,0,0));
    104                 buttonPanel.add(editAnno, GBC.std().insets(5,5,5,0));
    105                 buttonPanel.add(deleteAnno, GBC.std().insets(0,5,0,0));
    106                 gui.map.add(tpPanel, GBC.eol().fill(GBC.BOTH));
    107         }
     77        JButton deleteAnno = new JButton(tr("Delete"));
     78        deleteAnno.addActionListener(new ActionListener(){
     79            public void actionPerformed(ActionEvent e) {
     80                if (taggingPresetSources.getSelectedIndex() == -1)
     81                    JOptionPane.showMessageDialog(Main.parent, tr("Please select the row to delete."));
     82                else {
     83                    ((DefaultListModel)taggingPresetSources.getModel()).remove(taggingPresetSources.getSelectedIndex());
     84                    gui.requiresRestart = true;
     85                }
     86            }
     87        });
     88        taggingPresetSources.setVisibleRowCount(3);
    10889
    109         public void ok() {
    110                 Main.pref.put("taggingpreset.enable-defaults", enableDefault.getSelectedObjects() != null);
    111                 if (taggingPresetSources.getModel().getSize() > 0) {
    112                         StringBuilder sb = new StringBuilder();
    113                         for (int i = 0; i < taggingPresetSources.getModel().getSize(); ++i)
    114                                 sb.append(";"+taggingPresetSources.getModel().getElementAt(i));
    115                         Main.pref.put("taggingpreset.sources", sb.toString().substring(1));
    116                 } else
    117                         Main.pref.put("taggingpreset.sources", null);
    118         }
     90        taggingPresetSources.setToolTipText(tr("The sources (url or filename) of tagging preset definition files. See http://josm.openstreetmap.de/wiki/TaggingPresets for help."));
     91        addAnno.setToolTipText(tr("Add a new tagging preset source to the list."));
     92        deleteAnno.setToolTipText(tr("Delete the selected source from the list."));
    11993
    120         /**
    121          * Initialize the tagging presets (load and may display error)
    122          */
    123         public static void initialize() {
    124                 taggingPresets = TaggingPreset.readFromPreferences();
    125                 if (taggingPresets.isEmpty()) {
    126                         Main.main.menu.presetsMenu.setVisible(false);
    127                 }
    128                 else
    129                 {
    130                         HashMap<TaggingPresetMenu,JMenu> submenus = new HashMap<TaggingPresetMenu,JMenu>();
    131                         for (final TaggingPreset p : taggingPresets)
    132                         {
    133                                 JMenu m = p.group != null ? submenus.get(p.group) : Main.main.menu.presetsMenu;
    134                                 if (p instanceof TaggingPresetSeparator)
    135                                         m.add(new JSeparator());
    136                                 else if (p instanceof TaggingPresetMenu)
    137                                 {
    138                                         JMenu submenu = new JMenu(p);
    139                                         ((TaggingPresetMenu)p).menu = submenu;
    140                                         submenus.put((TaggingPresetMenu)p, submenu);
    141                                         m.add(submenu);
    142                                 }
    143                                 else
    144                                 {
    145                                         JMenuItem mi = new JMenuItem(p);
    146                                         mi.setText(tr(p.name));
    147                                         m.add(mi);
    148                                 }
    149                         }
    150                 }
    151         }
     94        JPanel tpPanel = new JPanel();
     95        tpPanel.setBorder(BorderFactory.createTitledBorder(BorderFactory.createLineBorder(Color.gray), tr("Tagging Presets")));
     96        tpPanel.setLayout(new GridBagLayout());
     97        tpPanel.add(enableDefault, GBC.eol().insets(5,5,5,0));
     98        tpPanel.add(new JLabel(tr("Tagging preset sources")), GBC.eol().insets(5,5,5,0));
     99        tpPanel.add(new JScrollPane(taggingPresetSources), GBC.eol().insets(5,0,5,0).fill(GBC.BOTH));
     100        JPanel buttonPanel = new JPanel(new GridBagLayout());
     101        tpPanel.add(buttonPanel, GBC.eol().insets(5,0,5,5).fill(GBC.HORIZONTAL));
     102        buttonPanel.add(Box.createHorizontalGlue(), GBC.std().fill(GBC.HORIZONTAL));
     103        buttonPanel.add(addAnno, GBC.std().insets(0,5,0,0));
     104        buttonPanel.add(editAnno, GBC.std().insets(5,5,5,0));
     105        buttonPanel.add(deleteAnno, GBC.std().insets(0,5,0,0));
     106        gui.map.add(tpPanel, GBC.eol().fill(GBC.BOTH));
     107    }
     108
     109    public void ok() {
     110        Main.pref.put("taggingpreset.enable-defaults", enableDefault.getSelectedObjects() != null);
     111        if (taggingPresetSources.getModel().getSize() > 0) {
     112            StringBuilder sb = new StringBuilder();
     113            for (int i = 0; i < taggingPresetSources.getModel().getSize(); ++i)
     114                sb.append(";"+taggingPresetSources.getModel().getElementAt(i));
     115            Main.pref.put("taggingpreset.sources", sb.toString().substring(1));
     116        } else
     117            Main.pref.put("taggingpreset.sources", null);
     118    }
     119
     120    /**
     121     * Initialize the tagging presets (load and may display error)
     122     */
     123    public static void initialize() {
     124        taggingPresets = TaggingPreset.readFromPreferences();
     125        if (taggingPresets.isEmpty()) {
     126            Main.main.menu.presetsMenu.setVisible(false);
     127        }
     128        else
     129        {
     130            HashMap<TaggingPresetMenu,JMenu> submenus = new HashMap<TaggingPresetMenu,JMenu>();
     131            for (final TaggingPreset p : taggingPresets)
     132            {
     133                JMenu m = p.group != null ? submenus.get(p.group) : Main.main.menu.presetsMenu;
     134                if (p instanceof TaggingPresetSeparator)
     135                    m.add(new JSeparator());
     136                else if (p instanceof TaggingPresetMenu)
     137                {
     138                    JMenu submenu = new JMenu(p);
     139                    ((TaggingPresetMenu)p).menu = submenu;
     140                    submenus.put((TaggingPresetMenu)p, submenu);
     141                    m.add(submenu);
     142                }
     143                else
     144                {
     145                    JMenuItem mi = new JMenuItem(p);
     146                    mi.setText(tr(p.name));
     147                    m.add(mi);
     148                }
     149            }
     150        }
     151    }
    152152}
  • trunk/src/org/openstreetmap/josm/gui/preferences/ToolbarPreferences.java

    r1053 r1169  
    3737public class ToolbarPreferences implements PreferenceSetting {
    3838
    39         private final class Move implements ActionListener {
    40                 public void actionPerformed(ActionEvent e) {
    41                         if (e.getActionCommand().equals("<<")) {
    42                                 while (unselected.size() > 1) {
    43                                         selected.addElement(unselected.get(0));
    44                                         unselected.remove(0);
    45                                 }
    46                         } else if (e.getActionCommand().equals("<") && unselectedList.getSelectedIndex() != -1) {
    47                                 while (unselectedList.getSelectedIndex() != -1 && unselectedList.getSelectedIndex() != unselected.size()-1) {
    48                                         selected.addElement(unselectedList.getSelectedValue());
    49                                         unselected.remove(unselectedList.getSelectedIndex());
    50                                 }
    51                                 if (unselectedList.getSelectedIndex() == unselected.size()-1)
    52                                         selected.addElement(null);
    53                         } else if (e.getActionCommand().equals(">") && selectedList.getSelectedIndex() != -1) {
    54                                 while (selectedList.getSelectedIndex() != -1) {
    55                                         if (selectedList.getSelectedValue() != null)
    56                                                 unselected.add(unselected.size()-1, selectedList.getSelectedValue());
    57                                         selected.remove(selectedList.getSelectedIndex());
    58                                 }
    59                         } else if (e.getActionCommand().equals(">>")) {
    60                                 while (selected.size() > 0) {
    61                                         if (selected.get(0) != null)
    62                                                 unselected.add(unselected.size()-1, selected.get(0));
    63                                         selected.remove(0);
    64                                 }
    65                         } else if (e.getActionCommand().equals("up")) {
    66                                 int i = selectedList.getSelectedIndex();
    67                                 Object o = selected.get(i);
    68                                 if (i != 0) {
    69                                         selected.remove(i);
    70                                         selected.add(i-1, o);
    71                                         selectedList.setSelectedIndex(i-1);
    72                                 }
    73                         } else if (e.getActionCommand().equals("down")) {
    74                                 int i = selectedList.getSelectedIndex();
    75                                 Object o = selected.get(i);
    76                                 if (i != selected.size()-1) {
    77                                         selected.remove(i);
    78                                         selected.add(i+1, o);
    79                                         selectedList.setSelectedIndex(i+1);
    80                                 }
    81                         }
    82                 }
    83         }
    84         private Move moveAction = new Move();
    85 
    86         /**
    87         * Key: Registered name (property "toolbar" of action).
    88         * Value: The action to execute.
    89         */
    90         private Map<String, Action> actions = new HashMap<String, Action>();
    91 
    92         private DefaultListModel selected = new DefaultListModel();
    93         private DefaultListModel unselected = new DefaultListModel();
    94         private JList selectedList = new JList(selected);
    95         private JList unselectedList = new JList(unselected);
    96 
    97         public JToolBar control = new JToolBar();
    98 
    99         private JButton upButton;
    100         private JButton downButton;
    101 
    102         public ToolbarPreferences() {
    103                 control.setFloatable(false);
    104 
    105                 final ListCellRenderer oldRenderer = selectedList.getCellRenderer();
    106                 ListCellRenderer renderer = new DefaultListCellRenderer(){
    107                         @Override public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
    108                                 String s = tr("Separator");
    109                                 Icon i = ImageProvider.get("preferences/separator");
    110                                 if (value != null) {
    111                                         s = (String)((Action)value).getValue(Action.NAME);
    112                                         i = (Icon)((Action)value).getValue(Action.SMALL_ICON);
    113                                 }
    114                                 JLabel l = (JLabel)oldRenderer.getListCellRendererComponent(list, s, index, isSelected, cellHasFocus);
    115                                 l.setIcon(i);
    116                                 return l;
    117                         }
    118                 };
    119                 selectedList.setCellRenderer(renderer);
    120                 unselectedList.setCellRenderer(renderer);
    121 
    122                 unselectedList.addListSelectionListener(new ListSelectionListener(){
    123                         public void valueChanged(ListSelectionEvent e) {
    124                                 if ((unselectedList.getSelectedIndex() != -1))
    125                                         selectedList.clearSelection();
    126                                 upButton.setEnabled(selectedList.getSelectedIndex() != -1);
    127                                 downButton.setEnabled(selectedList.getSelectedIndex() != -1);
    128                         }
    129                 });
    130                 selectedList.addListSelectionListener(new ListSelectionListener(){
    131                         public void valueChanged(ListSelectionEvent e) {
    132                                 boolean sel = selectedList.getSelectedIndex() != -1;
    133                                 if (sel)
    134                                         unselectedList.clearSelection();
    135                                 upButton.setEnabled(sel);
    136                                 downButton.setEnabled(sel);
    137                         }
    138                 });
    139         }
    140 
    141         public void addGui(PreferenceDialog gui) {
    142                 selected.removeAllElements();
    143                 unselected.removeAllElements();
    144                 Map<String, Action> us = new TreeMap<String, Action>();
    145                 for (Action a : actions.values())
    146                 {
    147                         us.put(a.getValue(Action.NAME).toString()+a.toString(), a);
    148                 }
    149                 for (String a : us.keySet())
    150                         unselected.addElement(us.get(a));
    151                 unselected.addElement(null);
    152 
    153                 final JPanel left = new JPanel(new GridBagLayout());
    154                 left.add(new JLabel(tr("Toolbar")), GBC.eol());
    155                 left.add(new JScrollPane(selectedList), GBC.std().fill(GBC.BOTH));
    156 
    157                 final JPanel right = new JPanel(new GridBagLayout());
    158                 right.add(new JLabel(tr("Available")), GBC.eol());
    159                 right.add(new JScrollPane(unselectedList), GBC.eol().fill(GBC.BOTH));
    160 
    161                 final JPanel buttons = new JPanel(new GridLayout(6,1));
    162                 buttons.add(upButton = createButton("up"));
    163                 buttons.add(createButton("<<"));
    164                 buttons.add(createButton("<"));
    165                 buttons.add(createButton(">"));
    166                 buttons.add(createButton(">>"));
    167                 buttons.add(downButton = createButton("down"));
    168                 upButton.setEnabled(false);
    169                 downButton.setEnabled(false);
    170                
    171                 final JPanel p = new JPanel();
    172                 p.setLayout(new LayoutManager(){
    173                         public void addLayoutComponent(String name, Component comp) {}
    174                         public void removeLayoutComponent(Component comp) {}
    175                         public Dimension minimumLayoutSize(Container parent) {
    176                                 Dimension l = left.getMinimumSize();
    177                                 Dimension r = right.getMinimumSize();
    178                                 Dimension b = buttons.getMinimumSize();
    179                                 return new Dimension(l.width+b.width+10+r.width,l.height+b.height+10+r.height);
    180                         }
    181                         public Dimension preferredLayoutSize(Container parent) {
    182                                 Dimension l = left.getPreferredSize();
    183                                 Dimension r = right.getPreferredSize();
    184                                 return new Dimension(l.width+r.width+10+buttons.getPreferredSize().width,Math.max(l.height, r.height));
    185                         }
    186                         public void layoutContainer(Container parent) {
    187                                 Dimension d = p.getSize();
    188                                 Dimension b = buttons.getPreferredSize();
    189                                 int width = (d.width-10-b.width)/2;
    190                                 left.setBounds(new Rectangle(0,0,width,d.height));
    191                                 right.setBounds(new Rectangle(width+10+b.width,0,width,d.height));
    192                                 buttons.setBounds(new Rectangle(width+5, d.height/2-b.height/2, b.width, b.height));
    193                         }
    194                 });
    195                 p.add(left);
    196                 p.add(buttons);
    197                 p.add(right);
    198 
    199                 JPanel panel = gui.createPreferenceTab("toolbar", tr("Toolbar customization"),
    200                         tr("Customize the elements on the toolbar."), false);
    201                 panel.add(p, GBC.eol().fill(GBC.BOTH));
    202 
    203                 for (String s : getToolString()) {
    204                         if (s.equals("|"))
    205                                 selected.addElement(null);
    206                         else {
    207                                 Action a = actions.get(s);
    208                                 if (a != null) {
    209                                         selected.addElement(a);
    210                                         unselected.removeElement(a);
    211                                 }
    212                         }
    213                 }
    214         }
    215 
    216         private String[] getToolString() {
    217                 String s = Main.pref.get("toolbar", "open;save;exportgpx;|;download;upload;|;undo;redo;|;preference");
    218                 if (s == null || s.equals("null") || s.equals(""))
    219                         return new String[0];
    220                 return s.split(";");
    221         }
    222 
    223         private JButton createButton(String name) {
    224                 JButton b = new JButton();
    225                 if (name.equals("up"))
    226                         b.setIcon(ImageProvider.get("dialogs", "up"));
    227                 else if (name.equals("down"))
    228                         b.setIcon(ImageProvider.get("dialogs", "down"));
    229                 else
    230                         b.setText(name);
    231                 b.addActionListener(moveAction);
    232                 b.setActionCommand(name);
    233                 return b;
    234         }
    235 
    236         public void ok() {
    237                 StringBuilder b = new StringBuilder();
    238                 for (int i = 0; i < selected.size(); ++i) {
    239                         if (selected.get(i) == null)
    240                                 b.append("|");
    241                         else
    242                                 b.append(((Action)selected.get(i)).getValue("toolbar"));
    243                         b.append(";");
    244                 }
    245                 String s = b.toString();
    246                 if (s.length() > 0)
    247                         s = s.substring(0, s.length()-1);
    248                 else
    249                         s = "null";
    250                 Main.pref.put("toolbar", s);
    251                 refreshToolbarControl();
    252         }
    253 
    254         /**
    255         * @return The parameter (for better chaining)
    256         */
    257         public Action register(Action action) {
    258                 actions.put((String)action.getValue("toolbar"), action);
    259                 return action;
    260         }
    261 
    262         /**
    263         * Parse the toolbar preference setting and construct the toolbar GUI control.
    264          *
    265          * Call this, if anything has changed in the toolbar settings and you want to refresh
    266         * the toolbar content (e.g. after registering actions in a plugin)
    267         */
    268         public void refreshToolbarControl() {
    269                 control.removeAll();
    270                 for (String s : getToolString()) {
    271                         if (s.equals("|"))
    272                                 control.addSeparator();
    273                         else
    274                                 control.add(actions.get(s));
    275                 }
    276                 control.setVisible(control.getComponentCount() != 0);
    277         }
     39    private final class Move implements ActionListener {
     40        public void actionPerformed(ActionEvent e) {
     41            if (e.getActionCommand().equals("<<")) {
     42                while (unselected.size() > 1) {
     43                    selected.addElement(unselected.get(0));
     44                    unselected.remove(0);
     45                }
     46            } else if (e.getActionCommand().equals("<") && unselectedList.getSelectedIndex() != -1) {
     47                while (unselectedList.getSelectedIndex() != -1 && unselectedList.getSelectedIndex() != unselected.size()-1) {
     48                    selected.addElement(unselectedList.getSelectedValue());
     49                    unselected.remove(unselectedList.getSelectedIndex());
     50                }
     51                if (unselectedList.getSelectedIndex() == unselected.size()-1)
     52                    selected.addElement(null);
     53            } else if (e.getActionCommand().equals(">") && selectedList.getSelectedIndex() != -1) {
     54                while (selectedList.getSelectedIndex() != -1) {
     55                    if (selectedList.getSelectedValue() != null)
     56                        unselected.add(unselected.size()-1, selectedList.getSelectedValue());
     57                    selected.remove(selectedList.getSelectedIndex());
     58                }
     59            } else if (e.getActionCommand().equals(">>")) {
     60                while (selected.size() > 0) {
     61                    if (selected.get(0) != null)
     62                        unselected.add(unselected.size()-1, selected.get(0));
     63                    selected.remove(0);
     64                }
     65            } else if (e.getActionCommand().equals("up")) {
     66                int i = selectedList.getSelectedIndex();
     67                Object o = selected.get(i);
     68                if (i != 0) {
     69                    selected.remove(i);
     70                    selected.add(i-1, o);
     71                    selectedList.setSelectedIndex(i-1);
     72                }
     73            } else if (e.getActionCommand().equals("down")) {
     74                int i = selectedList.getSelectedIndex();
     75                Object o = selected.get(i);
     76                if (i != selected.size()-1) {
     77                    selected.remove(i);
     78                    selected.add(i+1, o);
     79                    selectedList.setSelectedIndex(i+1);
     80                }
     81            }
     82        }
     83    }
     84    private Move moveAction = new Move();
     85
     86    /**
     87    * Key: Registered name (property "toolbar" of action).
     88    * Value: The action to execute.
     89    */
     90    private Map<String, Action> actions = new HashMap<String, Action>();
     91
     92    private DefaultListModel selected = new DefaultListModel();
     93    private DefaultListModel unselected = new DefaultListModel();
     94    private JList selectedList = new JList(selected);
     95    private JList unselectedList = new JList(unselected);
     96
     97    public JToolBar control = new JToolBar();
     98
     99    private JButton upButton;
     100    private JButton downButton;
     101
     102    public ToolbarPreferences() {
     103        control.setFloatable(false);
     104
     105        final ListCellRenderer oldRenderer = selectedList.getCellRenderer();
     106        ListCellRenderer renderer = new DefaultListCellRenderer(){
     107            @Override public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
     108                String s = tr("Separator");
     109                Icon i = ImageProvider.get("preferences/separator");
     110                if (value != null) {
     111                    s = (String)((Action)value).getValue(Action.NAME);
     112                    i = (Icon)((Action)value).getValue(Action.SMALL_ICON);
     113                }
     114                JLabel l = (JLabel)oldRenderer.getListCellRendererComponent(list, s, index, isSelected, cellHasFocus);
     115                l.setIcon(i);
     116                return l;
     117            }
     118        };
     119        selectedList.setCellRenderer(renderer);
     120        unselectedList.setCellRenderer(renderer);
     121
     122        unselectedList.addListSelectionListener(new ListSelectionListener(){
     123            public void valueChanged(ListSelectionEvent e) {
     124                if ((unselectedList.getSelectedIndex() != -1))
     125                    selectedList.clearSelection();
     126                upButton.setEnabled(selectedList.getSelectedIndex() != -1);
     127                downButton.setEnabled(selectedList.getSelectedIndex() != -1);
     128            }
     129        });
     130        selectedList.addListSelectionListener(new ListSelectionListener(){
     131            public void valueChanged(ListSelectionEvent e) {
     132                boolean sel = selectedList.getSelectedIndex() != -1;
     133                if (sel)
     134                    unselectedList.clearSelection();
     135                upButton.setEnabled(sel);
     136                downButton.setEnabled(sel);
     137            }
     138        });
     139    }
     140
     141    public void addGui(PreferenceDialog gui) {
     142        selected.removeAllElements();
     143        unselected.removeAllElements();
     144        Map<String, Action> us = new TreeMap<String, Action>();
     145        for (Action a : actions.values())
     146        {
     147            us.put(a.getValue(Action.NAME).toString()+a.toString(), a);
     148        }
     149        for (String a : us.keySet())
     150            unselected.addElement(us.get(a));
     151        unselected.addElement(null);
     152
     153        final JPanel left = new JPanel(new GridBagLayout());
     154        left.add(new JLabel(tr("Toolbar")), GBC.eol());
     155        left.add(new JScrollPane(selectedList), GBC.std().fill(GBC.BOTH));
     156
     157        final JPanel right = new JPanel(new GridBagLayout());
     158        right.add(new JLabel(tr("Available")), GBC.eol());
     159        right.add(new JScrollPane(unselectedList), GBC.eol().fill(GBC.BOTH));
     160
     161        final JPanel buttons = new JPanel(new GridLayout(6,1));
     162        buttons.add(upButton = createButton("up"));
     163        buttons.add(createButton("<<"));
     164        buttons.add(createButton("<"));
     165        buttons.add(createButton(">"));
     166        buttons.add(createButton(">>"));
     167        buttons.add(downButton = createButton("down"));
     168        upButton.setEnabled(false);
     169        downButton.setEnabled(false);
     170
     171        final JPanel p = new JPanel();
     172        p.setLayout(new LayoutManager(){
     173            public void addLayoutComponent(String name, Component comp) {}
     174            public void removeLayoutComponent(Component comp) {}
     175            public Dimension minimumLayoutSize(Container parent) {
     176                Dimension l = left.getMinimumSize();
     177                Dimension r = right.getMinimumSize();
     178                Dimension b = buttons.getMinimumSize();
     179                return new Dimension(l.width+b.width+10+r.width,l.height+b.height+10+r.height);
     180            }
     181            public Dimension preferredLayoutSize(Container parent) {
     182                Dimension l = left.getPreferredSize();
     183                Dimension r = right.getPreferredSize();
     184                return new Dimension(l.width+r.width+10+buttons.getPreferredSize().width,Math.max(l.height, r.height));
     185            }
     186            public void layoutContainer(Container parent) {
     187                Dimension d = p.getSize();
     188                Dimension b = buttons.getPreferredSize();
     189                int width = (d.width-10-b.width)/2;
     190                left.setBounds(new Rectangle(0,0,width,d.height));
     191                right.setBounds(new Rectangle(width+10+b.width,0,width,d.height));
     192                buttons.setBounds(new Rectangle(width+5, d.height/2-b.height/2, b.width, b.height));
     193            }
     194        });
     195        p.add(left);
     196        p.add(buttons);
     197        p.add(right);
     198
     199        JPanel panel = gui.createPreferenceTab("toolbar", tr("Toolbar customization"),
     200                tr("Customize the elements on the toolbar."), false);
     201        panel.add(p, GBC.eol().fill(GBC.BOTH));
     202
     203        for (String s : getToolString()) {
     204            if (s.equals("|"))
     205                selected.addElement(null);
     206            else {
     207                Action a = actions.get(s);
     208                if (a != null) {
     209                    selected.addElement(a);
     210                    unselected.removeElement(a);
     211                }
     212            }
     213        }
     214    }
     215
     216    private String[] getToolString() {
     217        String s = Main.pref.get("toolbar", "open;save;exportgpx;|;download;upload;|;undo;redo;|;preference");
     218        if (s == null || s.equals("null") || s.equals(""))
     219            return new String[0];
     220        return s.split(";");
     221    }
     222
     223    private JButton createButton(String name) {
     224        JButton b = new JButton();
     225        if (name.equals("up"))
     226            b.setIcon(ImageProvider.get("dialogs", "up"));
     227        else if (name.equals("down"))
     228            b.setIcon(ImageProvider.get("dialogs", "down"));
     229        else
     230            b.setText(name);
     231        b.addActionListener(moveAction);
     232        b.setActionCommand(name);
     233        return b;
     234    }
     235
     236    public void ok() {
     237        StringBuilder b = new StringBuilder();
     238        for (int i = 0; i < selected.size(); ++i) {
     239            if (selected.get(i) == null)
     240                b.append("|");
     241            else
     242                b.append(((Action)selected.get(i)).getValue("toolbar"));
     243            b.append(";");
     244        }
     245        String s = b.toString();
     246        if (s.length() > 0)
     247            s = s.substring(0, s.length()-1);
     248        else
     249            s = "null";
     250        Main.pref.put("toolbar", s);
     251        refreshToolbarControl();
     252    }
     253
     254    /**
     255    * @return The parameter (for better chaining)
     256    */
     257    public Action register(Action action) {
     258        actions.put((String)action.getValue("toolbar"), action);
     259        return action;
     260    }
     261
     262    /**
     263    * Parse the toolbar preference setting and construct the toolbar GUI control.
     264     *
     265     * Call this, if anything has changed in the toolbar settings and you want to refresh
     266    * the toolbar content (e.g. after registering actions in a plugin)
     267    */
     268    public void refreshToolbarControl() {
     269        control.removeAll();
     270        for (String s : getToolString()) {
     271            if (s.equals("|"))
     272                control.addSeparator();
     273            else
     274                control.add(actions.get(s));
     275        }
     276        control.setVisible(control.getComponentCount() != 0);
     277    }
    278278}
  • trunk/src/org/openstreetmap/josm/gui/preferences/prefJPanel.java

    r1084 r1169  
    2929public class prefJPanel extends javax.swing.JPanel {
    3030
    31                 // table of shortcuts
    32                 private TableModel model;
    33                 // comboboxes of modifier groups, mapping selectedIndex to real data
    34                 private static int[] modifInts = new int[]{
    35                         -1,
    36                         0,
    37                         KeyEvent.SHIFT_DOWN_MASK,
    38                         KeyEvent.CTRL_DOWN_MASK,
    39                         KeyEvent.ALT_DOWN_MASK,
    40                         KeyEvent.META_DOWN_MASK,
    41                         KeyEvent.CTRL_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK,
    42                         KeyEvent.ALT_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK,
    43                         KeyEvent.META_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK,
    44                         KeyEvent.CTRL_DOWN_MASK | KeyEvent.ALT_DOWN_MASK,
    45                         KeyEvent.CTRL_DOWN_MASK | KeyEvent.META_DOWN_MASK,
    46                         KeyEvent.ALT_DOWN_MASK | KeyEvent.META_DOWN_MASK,
    47                         KeyEvent.CTRL_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK | KeyEvent.ALT_DOWN_MASK,
    48                         KeyEvent.META_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK | KeyEvent.ALT_DOWN_MASK
    49                 };
    50                 // and here are the texts fro the comboboxes
    51                 private static String[] modifList = new String[] {
    52                         tr("disabled"),
    53                         tr("no modifier"),
    54                         KeyEvent.getKeyModifiersText(javax.swing.KeyStroke.getKeyStroke(KeyEvent.VK_A, modifInts[2]).getModifiers()),
    55                         KeyEvent.getKeyModifiersText(javax.swing.KeyStroke.getKeyStroke(KeyEvent.VK_A, modifInts[3]).getModifiers()),
    56                         KeyEvent.getKeyModifiersText(javax.swing.KeyStroke.getKeyStroke(KeyEvent.VK_A, modifInts[4]).getModifiers()),
    57                         KeyEvent.getKeyModifiersText(javax.swing.KeyStroke.getKeyStroke(KeyEvent.VK_A, modifInts[5]).getModifiers()),
    58                         KeyEvent.getKeyModifiersText(javax.swing.KeyStroke.getKeyStroke(KeyEvent.VK_A, modifInts[6]).getModifiers()),
    59                         KeyEvent.getKeyModifiersText(javax.swing.KeyStroke.getKeyStroke(KeyEvent.VK_A, modifInts[7]).getModifiers()),
    60                         KeyEvent.getKeyModifiersText(javax.swing.KeyStroke.getKeyStroke(KeyEvent.VK_A, modifInts[8]).getModifiers()),
    61                         KeyEvent.getKeyModifiersText(javax.swing.KeyStroke.getKeyStroke(KeyEvent.VK_A, modifInts[9]).getModifiers()),
    62                         KeyEvent.getKeyModifiersText(javax.swing.KeyStroke.getKeyStroke(KeyEvent.VK_A, modifInts[10]).getModifiers()),
    63                         KeyEvent.getKeyModifiersText(javax.swing.KeyStroke.getKeyStroke(KeyEvent.VK_A, modifInts[11]).getModifiers()),
    64                         KeyEvent.getKeyModifiersText(javax.swing.KeyStroke.getKeyStroke(KeyEvent.VK_A, modifInts[12]).getModifiers()),
    65                         KeyEvent.getKeyModifiersText(javax.swing.KeyStroke.getKeyStroke(KeyEvent.VK_A, modifInts[13]).getModifiers())
    66                 };
    67                 // this are the display(!) texts for the checkboxes. Let the JVM do the i18n for us <g>.
    68                 // Ok, there's a real reason for this: The JVM should know best how the keys are labelled
    69                 // on the physical keyboard. What language pack is installed in JOSM is completely
    70                 // independent from the keyboard's labelling. But the operation system's locale
    71                 // usually matches the keyboard. This even works with my English Windows and my German
    72                 // keyboard.
    73                 private static String SHIFT = KeyEvent.getKeyModifiersText(javax.swing.KeyStroke.getKeyStroke(KeyEvent.VK_A, KeyEvent.SHIFT_DOWN_MASK).getModifiers());
    74                 private static String CTRL  = KeyEvent.getKeyModifiersText(javax.swing.KeyStroke.getKeyStroke(KeyEvent.VK_A, KeyEvent.CTRL_DOWN_MASK).getModifiers());
    75                 private static String ALT   = KeyEvent.getKeyModifiersText(javax.swing.KeyStroke.getKeyStroke(KeyEvent.VK_A, KeyEvent.ALT_DOWN_MASK).getModifiers());
    76                 private static String META  = KeyEvent.getKeyModifiersText(javax.swing.KeyStroke.getKeyStroke(KeyEvent.VK_A, KeyEvent.META_DOWN_MASK).getModifiers());
    77 
    78                 // A list of keys to present the user. Sadly this really is a list of keys Java knows about,
    79                 // not a list of real physical keys. If someone knows how to get that list?
    80                 private static Map<Integer, String> keyList = setKeyList();
    81 
    82                 private static Map<Integer, String> setKeyList() {
    83                         Map<Integer, String> list = new LinkedHashMap<Integer, String>();
    84                         // I hate this, but I found no alternative...
    85                         for (int i = 0; i < 65534; i++) {
    86                                 String s = KeyEvent.getKeyText(i);
    87                                 if (s != null && s.length() > 0 && !s.contains("Unknown")) {
    88                                         list.put(new Integer(i), s);
    89                                         //System.out.println(i+": "+s);
    90                                 }
    91                         }
    92                         list.put(new Integer(-1), "");
    93                         return list;
    94                 }
     31        // table of shortcuts
     32        private TableModel model;
     33        // comboboxes of modifier groups, mapping selectedIndex to real data
     34        private static int[] modifInts = new int[]{
     35            -1,
     36            0,
     37            KeyEvent.SHIFT_DOWN_MASK,
     38            KeyEvent.CTRL_DOWN_MASK,
     39            KeyEvent.ALT_DOWN_MASK,
     40            KeyEvent.META_DOWN_MASK,
     41            KeyEvent.CTRL_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK,
     42            KeyEvent.ALT_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK,
     43            KeyEvent.META_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK,
     44            KeyEvent.CTRL_DOWN_MASK | KeyEvent.ALT_DOWN_MASK,
     45            KeyEvent.CTRL_DOWN_MASK | KeyEvent.META_DOWN_MASK,
     46            KeyEvent.ALT_DOWN_MASK | KeyEvent.META_DOWN_MASK,
     47            KeyEvent.CTRL_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK | KeyEvent.ALT_DOWN_MASK,
     48            KeyEvent.META_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK | KeyEvent.ALT_DOWN_MASK
     49        };
     50        // and here are the texts fro the comboboxes
     51        private static String[] modifList = new String[] {
     52            tr("disabled"),
     53            tr("no modifier"),
     54            KeyEvent.getKeyModifiersText(javax.swing.KeyStroke.getKeyStroke(KeyEvent.VK_A, modifInts[2]).getModifiers()),
     55            KeyEvent.getKeyModifiersText(javax.swing.KeyStroke.getKeyStroke(KeyEvent.VK_A, modifInts[3]).getModifiers()),
     56            KeyEvent.getKeyModifiersText(javax.swing.KeyStroke.getKeyStroke(KeyEvent.VK_A, modifInts[4]).getModifiers()),
     57            KeyEvent.getKeyModifiersText(javax.swing.KeyStroke.getKeyStroke(KeyEvent.VK_A, modifInts[5]).getModifiers()),
     58            KeyEvent.getKeyModifiersText(javax.swing.KeyStroke.getKeyStroke(KeyEvent.VK_A, modifInts[6]).getModifiers()),
     59            KeyEvent.getKeyModifiersText(javax.swing.KeyStroke.getKeyStroke(KeyEvent.VK_A, modifInts[7]).getModifiers()),
     60            KeyEvent.getKeyModifiersText(javax.swing.KeyStroke.getKeyStroke(KeyEvent.VK_A, modifInts[8]).getModifiers()),
     61            KeyEvent.getKeyModifiersText(javax.swing.KeyStroke.getKeyStroke(KeyEvent.VK_A, modifInts[9]).getModifiers()),
     62            KeyEvent.getKeyModifiersText(javax.swing.KeyStroke.getKeyStroke(KeyEvent.VK_A, modifInts[10]).getModifiers()),
     63            KeyEvent.getKeyModifiersText(javax.swing.KeyStroke.getKeyStroke(KeyEvent.VK_A, modifInts[11]).getModifiers()),
     64            KeyEvent.getKeyModifiersText(javax.swing.KeyStroke.getKeyStroke(KeyEvent.VK_A, modifInts[12]).getModifiers()),
     65            KeyEvent.getKeyModifiersText(javax.swing.KeyStroke.getKeyStroke(KeyEvent.VK_A, modifInts[13]).getModifiers())
     66        };
     67        // this are the display(!) texts for the checkboxes. Let the JVM do the i18n for us <g>.
     68        // Ok, there's a real reason for this: The JVM should know best how the keys are labelled
     69        // on the physical keyboard. What language pack is installed in JOSM is completely
     70        // independent from the keyboard's labelling. But the operation system's locale
     71        // usually matches the keyboard. This even works with my English Windows and my German
     72        // keyboard.
     73        private static String SHIFT = KeyEvent.getKeyModifiersText(javax.swing.KeyStroke.getKeyStroke(KeyEvent.VK_A, KeyEvent.SHIFT_DOWN_MASK).getModifiers());
     74        private static String CTRL  = KeyEvent.getKeyModifiersText(javax.swing.KeyStroke.getKeyStroke(KeyEvent.VK_A, KeyEvent.CTRL_DOWN_MASK).getModifiers());
     75        private static String ALT   = KeyEvent.getKeyModifiersText(javax.swing.KeyStroke.getKeyStroke(KeyEvent.VK_A, KeyEvent.ALT_DOWN_MASK).getModifiers());
     76        private static String META  = KeyEvent.getKeyModifiersText(javax.swing.KeyStroke.getKeyStroke(KeyEvent.VK_A, KeyEvent.META_DOWN_MASK).getModifiers());
     77
     78        // A list of keys to present the user. Sadly this really is a list of keys Java knows about,
     79        // not a list of real physical keys. If someone knows how to get that list?
     80        private static Map<Integer, String> keyList = setKeyList();
     81
     82        private static Map<Integer, String> setKeyList() {
     83            Map<Integer, String> list = new LinkedHashMap<Integer, String>();
     84            // I hate this, but I found no alternative...
     85            for (int i = 0; i < 65534; i++) {
     86                String s = KeyEvent.getKeyText(i);
     87                if (s != null && s.length() > 0 && !s.contains("Unknown")) {
     88                    list.put(new Integer(i), s);
     89                    //System.out.println(i+": "+s);
     90                }
     91            }
     92            list.put(new Integer(-1), "");
     93            return list;
     94        }
    9595
    9696    /** Creates new form prefJPanel */
    9797    // Ain't those auto-generated comments helpful or what? <g>
    9898    public prefJPanel(TableModel model) {
    99         this.model = model;
     99        this.model = model;
    100100        initComponents();
    101101    }
     
    156156        setLayout(new javax.swing.BoxLayout(this, javax.swing.BoxLayout.Y_AXIS));
    157157
    158                                 // If someone wants to move this text into some resource, feel free.
    159                                 infoTab.setLayout(new javax.swing.BoxLayout(shortcutTab, javax.swing.BoxLayout.Y_AXIS));
    160                                 JEditorPane editor = new JEditorPane();
    161                                 editor.setEditable(false);
    162                                 editor.setContentType("text/html");
    163                                 editor.setText(
    164                                         tr("<h1><a name=\"top\">Keyboard Shortcuts</a></h1>")+
    165                                         tr("<p>Please note that shortcuts keys are assigned to the actions when JOSM is started. So you need to <b>restart</b> "
    166                                           +"JOSM to see your changes.</p>")+
    167                                         tr("<p>Furthermore, the shortcuts are activated when the actions are assigned to a menu entry of button for the first "
    168                                           +"time. So some of your changes may become active even without restart --- but also without collistion handling. "
    169                                           +"This is another reason to <b>restart</b> JOSM after making any changes here.</p>")+
    170                                         tr("<p>You may notice that the key selection list on the next page lists all keys that exist on all kinds of keyboards "
    171                                           +"Java knows about, not just those keys that exist on your keyboard. Please use only those values that correspond to "
    172                                           +"a real key on your keyboard. So if your keyboard has no 'Copy' key (PC keyboard don't have them, Sun keyboards do), "
    173                                           +"the do not use it. Also there will be 'keys' listed that correspond to a shortcut on your keyboard (e.g. ':'/Colon). "
    174                                           +"Please also do not use them, use the base key (';'/Semicolon on US keyboards, '.'/Period on German keyboards, ...) "
    175                                           +"instead. Not doing so may result in conflicts, as there is no way for JOSM to know that Ctrl+Shift+; and Ctrl+: "
    176                                           +"actually is the same thing on an US keyboard...</p>")+
    177                                         tr("<p>Thank you for your understanding</p>")+
    178                                         tr("<h1>Modifier Groups</h1>")+
    179                                         tr("<p>The last page lists the modifier keys JOSM will automatically assign to shortcuts. For every of the four kinds "
    180                                           +"of shortcuts there are three alternatives. JOSM will try those alternative in the listed order when managing a "
    181                                           +"conflict. If all alternatives would result in shortcuts that are already taken, it will assign a random shortcut "
    182                                           +"instead.</p>")+
    183                                         tr("<p>The pseudo-modifier 'disabled' will disable the shortcut when encountered.</p>")
    184                                 );
    185                                 editor.setCaretPosition(0); // scroll up
    186                                 prefTabPane.addTab(tr("Read First"), new JScrollPane(editor));
     158                // If someone wants to move this text into some resource, feel free.
     159                infoTab.setLayout(new javax.swing.BoxLayout(shortcutTab, javax.swing.BoxLayout.Y_AXIS));
     160                JEditorPane editor = new JEditorPane();
     161                editor.setEditable(false);
     162                editor.setContentType("text/html");
     163                editor.setText(
     164                    tr("<h1><a name=\"top\">Keyboard Shortcuts</a></h1>")+
     165                    tr("<p>Please note that shortcuts keys are assigned to the actions when JOSM is started. So you need to <b>restart</b> "
     166                      +"JOSM to see your changes.</p>")+
     167                    tr("<p>Furthermore, the shortcuts are activated when the actions are assigned to a menu entry of button for the first "
     168                      +"time. So some of your changes may become active even without restart --- but also without collistion handling. "
     169                      +"This is another reason to <b>restart</b> JOSM after making any changes here.</p>")+
     170                    tr("<p>You may notice that the key selection list on the next page lists all keys that exist on all kinds of keyboards "
     171                      +"Java knows about, not just those keys that exist on your keyboard. Please use only those values that correspond to "
     172                      +"a real key on your keyboard. So if your keyboard has no 'Copy' key (PC keyboard don't have them, Sun keyboards do), "
     173                      +"the do not use it. Also there will be 'keys' listed that correspond to a shortcut on your keyboard (e.g. ':'/Colon). "
     174                      +"Please also do not use them, use the base key (';'/Semicolon on US keyboards, '.'/Period on German keyboards, ...) "
     175                      +"instead. Not doing so may result in conflicts, as there is no way for JOSM to know that Ctrl+Shift+; and Ctrl+: "
     176                      +"actually is the same thing on an US keyboard...</p>")+
     177                    tr("<p>Thank you for your understanding</p>")+
     178                    tr("<h1>Modifier Groups</h1>")+
     179                    tr("<p>The last page lists the modifier keys JOSM will automatically assign to shortcuts. For every of the four kinds "
     180                      +"of shortcuts there are three alternatives. JOSM will try those alternative in the listed order when managing a "
     181                      +"conflict. If all alternatives would result in shortcuts that are already taken, it will assign a random shortcut "
     182                      +"instead.</p>")+
     183                    tr("<p>The pseudo-modifier 'disabled' will disable the shortcut when encountered.</p>")
     184                );
     185                editor.setCaretPosition(0); // scroll up
     186                prefTabPane.addTab(tr("Read First"), new JScrollPane(editor));
    187187
    188188        shortcutTab.setLayout(new javax.swing.BoxLayout(shortcutTab, javax.swing.BoxLayout.Y_AXIS));
     
    342342        subwindowGroupPane.add(bxTer4);
    343343
    344                                 initbx();
    345                                 bxPrim1.setAction(action2);
    346                                 bxSec1.setAction(action2);
    347                                 bxTer1.setAction(action2);
    348                                 bxPrim2.setAction(action2);
    349                                 bxSec2.setAction(action2);
    350                                 bxTer2.setAction(action2);
    351                                 bxPrim3.setAction(action2);
    352                                 bxSec3.setAction(action2);
    353                                 bxTer3.setAction(action2);
    354                                 bxPrim4.setAction(action2);
    355                                 bxSec4.setAction(action2);
    356                                 bxTer4.setAction(action2);
    357 
    358                 modifierTab.add(subwindowGroupPane);
     344                initbx();
     345                bxPrim1.setAction(action2);
     346                bxSec1.setAction(action2);
     347                bxTer1.setAction(action2);
     348                bxPrim2.setAction(action2);
     349                bxSec2.setAction(action2);
     350                bxTer2.setAction(action2);
     351                bxPrim3.setAction(action2);
     352                bxSec3.setAction(action2);
     353                bxTer3.setAction(action2);
     354                bxPrim4.setAction(action2);
     355                bxSec4.setAction(action2);
     356                bxTer4.setAction(action2);
     357
     358        modifierTab.add(subwindowGroupPane);
    359359
    360360        prefTabPane.addTab(tr("Modifier Groups"), modifierScroller);
     
    363363    }
    364364
    365         // this allows to edit shortcuts. it:
    366         //  * sets the edit controls to the selected shortcut
    367         //  * enabled/disables the controls as needed
    368         //  * writes the user's changes to the shortcut
    369         // And after I finally had it working, I realized that those two methods
    370         // are playing ping-pong (politically correct: table tennis, I know) and
    371         // even have some duplicated code. Feel free to refactor, If you have
    372         // more expirience with GUI coding than I have.
    373         private class cbAction extends javax.swing.AbstractAction implements ListSelectionListener {
    374                 private prefJPanel panel;
    375                         public cbAction (prefJPanel panel) {
    376                                 this.panel = panel;
    377                 }
    378                 public void valueChanged(ListSelectionEvent e) {
    379                         ListSelectionModel lsm = panel.shortcutTable.getSelectionModel(); // can't use e here
    380                         if (!lsm.isSelectionEmpty()) {
    381                                 int row = lsm.getMinSelectionIndex();
    382                                 Shortcut sc = (Shortcut)panel.model.getValueAt(row, -1);
    383                                 panel.cbDefault.setSelected(!sc.getAssignedUser());
    384                                 panel.cbDisable.setSelected(sc.getKeyStroke() == null);
    385                                 panel.cbShift.setSelected(sc.getAssignedModifier() != -1 && (sc.getAssignedModifier() & KeyEvent.SHIFT_DOWN_MASK) != 0);
    386                                 panel.cbCtrl.setSelected(sc.getAssignedModifier() != -1 && (sc.getAssignedModifier() & KeyEvent.CTRL_DOWN_MASK) != 0);
    387                                 panel.cbAlt.setSelected(sc.getAssignedModifier() != -1 && (sc.getAssignedModifier() & KeyEvent.ALT_DOWN_MASK) != 0);
    388                                 panel.cbMeta.setSelected(sc.getAssignedModifier() != -1 && (sc.getAssignedModifier() & KeyEvent.META_DOWN_MASK) != 0);
    389                                 if (sc.getKeyStroke() != null) {
    390                                         tfKey.setSelectedItem(keyList.get(sc.getKeyStroke().getKeyCode()));
    391                                 } else {
    392                                         tfKey.setSelectedItem(keyList.get(-1));
    393                                 }
    394                                 if (sc.getAutomatic()) {
    395                                         panel.cbDefault.setEnabled(false);
    396                                         panel.cbDisable.setEnabled(false);
    397                                         panel.cbShift.setEnabled(false);
    398                                         panel.cbCtrl.setEnabled(false);
    399                                         panel.cbAlt.setEnabled(false);
    400                                         panel.cbMeta.setEnabled(false);
    401                                         panel.tfKey.setEnabled(false);
    402                                 } else {
    403                                         panel.cbDefault.setEnabled(true);
    404                                         actionPerformed(null);
    405                                 }
    406                         } else {
    407                                 panel.cbDefault.setEnabled(false);
    408                                 panel.cbDisable.setEnabled(false);
    409                                 panel.cbShift.setEnabled(false);
    410                                 panel.cbCtrl.setEnabled(false);
    411                                 panel.cbAlt.setEnabled(false);
    412                                 panel.cbMeta.setEnabled(false);
    413                                 panel.tfKey.setEnabled(false);
    414                         }
    415                 }
    416                 public void actionPerformed(java.awt.event.ActionEvent e) {
    417                         ListSelectionModel lsm = panel.shortcutTable.getSelectionModel();
    418                         if (lsm != null && !lsm.isSelectionEmpty()) {
    419                                 if (e != null) { // only if we've been called by a user action
    420                                         int row = lsm.getMinSelectionIndex();
    421                                         Shortcut sc = (Shortcut)panel.model.getValueAt(row, -1);
    422                                         sc.setAssignedUser(!panel.cbDefault.isSelected());
    423                                         if (panel.cbDisable.isSelected()) {
    424                                                 sc.setAssignedModifier(-1);
    425                                         } else if (panel.tfKey.getSelectedItem().equals("")) {
    426                                                 sc.setAssignedModifier(KeyEvent.VK_CANCEL);
    427                                         } else {
    428                                                 sc.setAssignedModifier(
    429                                                         (panel.cbShift.isSelected() ? KeyEvent.SHIFT_DOWN_MASK : 0) |
    430                                                         (panel.cbCtrl.isSelected() ? KeyEvent.CTRL_DOWN_MASK : 0) |
    431                                                         (panel.cbAlt.isSelected() ? KeyEvent.ALT_DOWN_MASK : 0) |
    432                                                         (panel.cbMeta.isSelected() ? KeyEvent.META_DOWN_MASK : 0)
    433                                                 );
    434                                                 for (Map.Entry<Integer, String> entry : keyList.entrySet()) {
    435                                                         if (entry.getValue().equals(panel.tfKey.getSelectedItem())) {
    436                                                                 sc.setAssignedKey(entry.getKey());
    437                                                         }
    438                                                 }
    439                                         }
    440                                         valueChanged(null);
    441                                 }
    442                                 boolean state = !panel.cbDefault.isSelected();
    443                                 panel.cbDisable.setEnabled(state);
    444                                 state = state && !panel.cbDisable.isSelected();
    445                                 panel.cbShift.setEnabled(state);
    446                                 panel.cbCtrl.setEnabled(state);
    447                                 panel.cbAlt.setEnabled(state);
    448                                 panel.cbMeta.setEnabled(state);
    449                                 panel.tfKey.setEnabled(state);
    450                         } else {
    451                                 panel.cbDefault.setEnabled(false);
    452                                 panel.cbDisable.setEnabled(false);
    453                                 panel.cbShift.setEnabled(false);
    454                                 panel.cbCtrl.setEnabled(false);
    455                                 panel.cbAlt.setEnabled(false);
    456                                 panel.cbMeta.setEnabled(false);
    457                                 panel.tfKey.setEnabled(false);
    458                         }
    459                 }
    460         }
    461 
    462         // this handles the modifier groups
    463         private class bxAction extends javax.swing.AbstractAction {
    464                 public void actionPerformed(java.awt.event.ActionEvent e) {
    465                         Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_DEFAULT+Shortcut.GROUP_EDIT),    Integer.toString( modifInts[bxPrim1.getSelectedIndex()] ));
    466                         Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT1   +Shortcut.GROUP_EDIT),    Integer.toString( modifInts[ bxSec1.getSelectedIndex()] ));
    467                         Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT2   +Shortcut.GROUP_EDIT),    Integer.toString( modifInts[ bxTer1.getSelectedIndex()] ));
    468 
    469                         Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_DEFAULT+Shortcut.GROUP_MENU),    Integer.toString( modifInts[bxPrim2.getSelectedIndex()] ));
    470                         Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT1   +Shortcut.GROUP_MENU),    Integer.toString( modifInts[ bxSec2.getSelectedIndex()] ));
    471                         Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT2   +Shortcut.GROUP_MENU),    Integer.toString( modifInts[ bxTer2.getSelectedIndex()] ));
    472 
    473                         Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_DEFAULT+Shortcut.GROUP_HOTKEY),  Integer.toString( modifInts[bxPrim3.getSelectedIndex()] ));
    474                         Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT1   +Shortcut.GROUP_HOTKEY),  Integer.toString( modifInts[ bxSec3.getSelectedIndex()] ));
    475                         Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT2   +Shortcut.GROUP_HOTKEY),  Integer.toString( modifInts[ bxTer3.getSelectedIndex()] ));
    476 
    477                         Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_DEFAULT+Shortcut.GROUP_LAYER),   Integer.toString( modifInts[bxPrim4.getSelectedIndex()] ));
    478                         Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT1   +Shortcut.GROUP_LAYER),   Integer.toString( modifInts[ bxSec4.getSelectedIndex()] ));
    479                         Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT2   +Shortcut.GROUP_LAYER),   Integer.toString( modifInts[ bxTer4.getSelectedIndex()] ));
    480                 }
    481         }
    482 
    483         private void initbx() {
    484                 setBx(bxPrim1, "shortcut.groups."+(Shortcut.GROUPS_DEFAULT+Shortcut.GROUP_EDIT));
    485                 setBx(bxSec1,  "shortcut.groups."+(Shortcut.GROUPS_ALT1   +Shortcut.GROUP_EDIT));
    486                 setBx(bxTer1,  "shortcut.groups."+(Shortcut.GROUPS_ALT2   +Shortcut.GROUP_EDIT));
    487 
    488                 setBx(bxPrim2, "shortcut.groups."+(Shortcut.GROUPS_DEFAULT+Shortcut.GROUP_MENU));
    489                 setBx(bxSec2,  "shortcut.groups."+(Shortcut.GROUPS_ALT1   +Shortcut.GROUP_MENU));
    490                 setBx(bxTer2,  "shortcut.groups."+(Shortcut.GROUPS_ALT2   +Shortcut.GROUP_MENU));
    491 
    492                 setBx(bxPrim3, "shortcut.groups."+(Shortcut.GROUPS_DEFAULT+Shortcut.GROUP_HOTKEY));
    493                 setBx(bxSec3,  "shortcut.groups."+(Shortcut.GROUPS_ALT1   +Shortcut.GROUP_HOTKEY));
    494                 setBx(bxTer3,  "shortcut.groups."+(Shortcut.GROUPS_ALT2   +Shortcut.GROUP_HOTKEY));
    495 
    496                 setBx(bxPrim4, "shortcut.groups."+(Shortcut.GROUPS_DEFAULT+Shortcut.GROUP_LAYER));
    497                 setBx(bxSec4,  "shortcut.groups."+(Shortcut.GROUPS_ALT1   +Shortcut.GROUP_LAYER));
    498                 setBx(bxTer4,  "shortcut.groups."+(Shortcut.GROUPS_ALT2   +Shortcut.GROUP_LAYER));
    499         }
    500         private void setBx(javax.swing.JComboBox bx, String key) {
    501                 int target = Main.pref.getInteger(key, -1);
    502                 for (int i = 0; i < modifInts.length; i++) {
    503                         if (modifInts[i] == target) {
    504                                 bx.setSelectedIndex(i);
    505                         }
    506                 }
    507         }
     365    // this allows to edit shortcuts. it:
     366    //  * sets the edit controls to the selected shortcut
     367    //  * enabled/disables the controls as needed
     368    //  * writes the user's changes to the shortcut
     369    // And after I finally had it working, I realized that those two methods
     370    // are playing ping-pong (politically correct: table tennis, I know) and
     371    // even have some duplicated code. Feel free to refactor, If you have
     372    // more expirience with GUI coding than I have.
     373    private class cbAction extends javax.swing.AbstractAction implements ListSelectionListener {
     374        private prefJPanel panel;
     375            public cbAction (prefJPanel panel) {
     376                this.panel = panel;
     377        }
     378        public void valueChanged(ListSelectionEvent e) {
     379            ListSelectionModel lsm = panel.shortcutTable.getSelectionModel(); // can't use e here
     380            if (!lsm.isSelectionEmpty()) {
     381                int row = lsm.getMinSelectionIndex();
     382                Shortcut sc = (Shortcut)panel.model.getValueAt(row, -1);
     383                panel.cbDefault.setSelected(!sc.getAssignedUser());
     384                panel.cbDisable.setSelected(sc.getKeyStroke() == null);
     385                panel.cbShift.setSelected(sc.getAssignedModifier() != -1 && (sc.getAssignedModifier() & KeyEvent.SHIFT_DOWN_MASK) != 0);
     386                panel.cbCtrl.setSelected(sc.getAssignedModifier() != -1 && (sc.getAssignedModifier() & KeyEvent.CTRL_DOWN_MASK) != 0);
     387                panel.cbAlt.setSelected(sc.getAssignedModifier() != -1 && (sc.getAssignedModifier() & KeyEvent.ALT_DOWN_MASK) != 0);
     388                panel.cbMeta.setSelected(sc.getAssignedModifier() != -1 && (sc.getAssignedModifier() & KeyEvent.META_DOWN_MASK) != 0);
     389                if (sc.getKeyStroke() != null) {
     390                    tfKey.setSelectedItem(keyList.get(sc.getKeyStroke().getKeyCode()));
     391                } else {
     392                    tfKey.setSelectedItem(keyList.get(-1));
     393                }
     394                if (sc.getAutomatic()) {
     395                    panel.cbDefault.setEnabled(false);
     396                    panel.cbDisable.setEnabled(false);
     397                    panel.cbShift.setEnabled(false);
     398                    panel.cbCtrl.setEnabled(false);
     399                    panel.cbAlt.setEnabled(false);
     400                    panel.cbMeta.setEnabled(false);
     401                    panel.tfKey.setEnabled(false);
     402                } else {
     403                    panel.cbDefault.setEnabled(true);
     404                    actionPerformed(null);
     405                }
     406            } else {
     407                panel.cbDefault.setEnabled(false);
     408                panel.cbDisable.setEnabled(false);
     409                panel.cbShift.setEnabled(false);
     410                panel.cbCtrl.setEnabled(false);
     411                panel.cbAlt.setEnabled(false);
     412                panel.cbMeta.setEnabled(false);
     413                panel.tfKey.setEnabled(false);
     414            }
     415        }
     416        public void actionPerformed(java.awt.event.ActionEvent e) {
     417            ListSelectionModel lsm = panel.shortcutTable.getSelectionModel();
     418            if (lsm != null && !lsm.isSelectionEmpty()) {
     419                if (e != null) { // only if we've been called by a user action
     420                    int row = lsm.getMinSelectionIndex();
     421                    Shortcut sc = (Shortcut)panel.model.getValueAt(row, -1);
     422                    sc.setAssignedUser(!panel.cbDefault.isSelected());
     423                    if (panel.cbDisable.isSelected()) {
     424                        sc.setAssignedModifier(-1);
     425                    } else if (panel.tfKey.getSelectedItem().equals("")) {
     426                        sc.setAssignedModifier(KeyEvent.VK_CANCEL);
     427                    } else {
     428                        sc.setAssignedModifier(
     429                            (panel.cbShift.isSelected() ? KeyEvent.SHIFT_DOWN_MASK : 0) |
     430                            (panel.cbCtrl.isSelected() ? KeyEvent.CTRL_DOWN_MASK : 0) |
     431                            (panel.cbAlt.isSelected() ? KeyEvent.ALT_DOWN_MASK : 0) |
     432                            (panel.cbMeta.isSelected() ? KeyEvent.META_DOWN_MASK : 0)
     433                        );
     434                        for (Map.Entry<Integer, String> entry : keyList.entrySet()) {
     435                            if (entry.getValue().equals(panel.tfKey.getSelectedItem())) {
     436                                sc.setAssignedKey(entry.getKey());
     437                            }
     438                        }
     439                    }
     440                    valueChanged(null);
     441                }
     442                boolean state = !panel.cbDefault.isSelected();
     443                panel.cbDisable.setEnabled(state);
     444                state = state && !panel.cbDisable.isSelected();
     445                panel.cbShift.setEnabled(state);
     446                panel.cbCtrl.setEnabled(state);
     447                panel.cbAlt.setEnabled(state);
     448                panel.cbMeta.setEnabled(state);
     449                panel.tfKey.setEnabled(state);
     450            } else {
     451                panel.cbDefault.setEnabled(false);
     452                panel.cbDisable.setEnabled(false);
     453                panel.cbShift.setEnabled(false);
     454                panel.cbCtrl.setEnabled(false);
     455                panel.cbAlt.setEnabled(false);
     456                panel.cbMeta.setEnabled(false);
     457                panel.tfKey.setEnabled(false);
     458            }
     459        }
     460    }
     461
     462    // this handles the modifier groups
     463    private class bxAction extends javax.swing.AbstractAction {
     464        public void actionPerformed(java.awt.event.ActionEvent e) {
     465            Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_DEFAULT+Shortcut.GROUP_EDIT),    Integer.toString( modifInts[bxPrim1.getSelectedIndex()] ));
     466            Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT1   +Shortcut.GROUP_EDIT),    Integer.toString( modifInts[ bxSec1.getSelectedIndex()] ));
     467            Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT2   +Shortcut.GROUP_EDIT),    Integer.toString( modifInts[ bxTer1.getSelectedIndex()] ));
     468
     469            Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_DEFAULT+Shortcut.GROUP_MENU),    Integer.toString( modifInts[bxPrim2.getSelectedIndex()] ));
     470            Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT1   +Shortcut.GROUP_MENU),    Integer.toString( modifInts[ bxSec2.getSelectedIndex()] ));
     471            Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT2   +Shortcut.GROUP_MENU),    Integer.toString( modifInts[ bxTer2.getSelectedIndex()] ));
     472
     473            Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_DEFAULT+Shortcut.GROUP_HOTKEY),  Integer.toString( modifInts[bxPrim3.getSelectedIndex()] ));
     474            Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT1   +Shortcut.GROUP_HOTKEY),  Integer.toString( modifInts[ bxSec3.getSelectedIndex()] ));
     475            Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT2   +Shortcut.GROUP_HOTKEY),  Integer.toString( modifInts[ bxTer3.getSelectedIndex()] ));
     476
     477            Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_DEFAULT+Shortcut.GROUP_LAYER),   Integer.toString( modifInts[bxPrim4.getSelectedIndex()] ));
     478            Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT1   +Shortcut.GROUP_LAYER),   Integer.toString( modifInts[ bxSec4.getSelectedIndex()] ));
     479            Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT2   +Shortcut.GROUP_LAYER),   Integer.toString( modifInts[ bxTer4.getSelectedIndex()] ));
     480        }
     481    }
     482
     483    private void initbx() {
     484        setBx(bxPrim1, "shortcut.groups."+(Shortcut.GROUPS_DEFAULT+Shortcut.GROUP_EDIT));
     485        setBx(bxSec1,  "shortcut.groups."+(Shortcut.GROUPS_ALT1   +Shortcut.GROUP_EDIT));
     486        setBx(bxTer1,  "shortcut.groups."+(Shortcut.GROUPS_ALT2   +Shortcut.GROUP_EDIT));
     487
     488        setBx(bxPrim2, "shortcut.groups."+(Shortcut.GROUPS_DEFAULT+Shortcut.GROUP_MENU));
     489        setBx(bxSec2,  "shortcut.groups."+(Shortcut.GROUPS_ALT1   +Shortcut.GROUP_MENU));
     490        setBx(bxTer2,  "shortcut.groups."+(Shortcut.GROUPS_ALT2   +Shortcut.GROUP_MENU));
     491
     492        setBx(bxPrim3, "shortcut.groups."+(Shortcut.GROUPS_DEFAULT+Shortcut.GROUP_HOTKEY));
     493        setBx(bxSec3,  "shortcut.groups."+(Shortcut.GROUPS_ALT1   +Shortcut.GROUP_HOTKEY));
     494        setBx(bxTer3,  "shortcut.groups."+(Shortcut.GROUPS_ALT2   +Shortcut.GROUP_HOTKEY));
     495
     496        setBx(bxPrim4, "shortcut.groups."+(Shortcut.GROUPS_DEFAULT+Shortcut.GROUP_LAYER));
     497        setBx(bxSec4,  "shortcut.groups."+(Shortcut.GROUPS_ALT1   +Shortcut.GROUP_LAYER));
     498        setBx(bxTer4,  "shortcut.groups."+(Shortcut.GROUPS_ALT2   +Shortcut.GROUP_LAYER));
     499    }
     500    private void setBx(javax.swing.JComboBox bx, String key) {
     501        int target = Main.pref.getInteger(key, -1);
     502        for (int i = 0; i < modifInts.length; i++) {
     503            if (modifInts[i] == target) {
     504                bx.setSelectedIndex(i);
     505            }
     506        }
     507    }
    508508
    509509    private javax.swing.JComboBox bxPrim1;
  • trunk/src/org/openstreetmap/josm/gui/tagging/ForwardActionListener.java

    r627 r1169  
    1313 */
    1414public final class ForwardActionListener implements ActionListener {
    15         public final TaggingPreset preset;
     15    public final TaggingPreset preset;
    1616
    17         private final PropertiesDialog propertiesDialog;
     17    private final PropertiesDialog propertiesDialog;
    1818
    19         public ForwardActionListener(PropertiesDialog propertiesDialog, TaggingPreset preset) {
    20                 this.propertiesDialog = propertiesDialog;
    21                 this.preset = preset;
    22         }
     19    public ForwardActionListener(PropertiesDialog propertiesDialog, TaggingPreset preset) {
     20        this.propertiesDialog = propertiesDialog;
     21        this.preset = preset;
     22    }
    2323
    24         public void actionPerformed(ActionEvent e) {
    25                 this.propertiesDialog.taggingPresets.setSelectedIndex(0);
    26                 e.setSource(this);
    27                 preset.actionPerformed(e);
    28         }
     24    public void actionPerformed(ActionEvent e) {
     25        this.propertiesDialog.taggingPresets.setSelectedIndex(0);
     26        e.setSource(this);
     27        preset.actionPerformed(e);
     28    }
    2929}
  • trunk/src/org/openstreetmap/josm/gui/tagging/TaggingCellRenderer.java

    r627 r1169  
    1515
    1616final public class TaggingCellRenderer extends DefaultListCellRenderer {
    17         @Override public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
    18                 TaggingPreset a = null;
    19                 if (value instanceof ForwardActionListener)
    20                         a = ((ForwardActionListener)value).preset;
    21                 else if (value instanceof TaggingPreset)
    22                         a = (TaggingPreset)value;
    23                 String name = a == null ? null : (String)a.getValue(Action.NAME);
    24                 if (name == null)
    25                         return super.getListCellRendererComponent(list, "", index, false, false);
    26                 JComponent c = (JComponent)super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
    27                 JLabel l = new JLabel(name);
    28                 l.setForeground(c.getForeground());
    29                 l.setBackground(c.getBackground());
    30                 l.setFont(c.getFont());
    31                 l.setBorder(c.getBorder());
    32                 ImageIcon icon = (ImageIcon)a.getValue(Action.SMALL_ICON);
    33                 if (icon != null)
    34                         l.setIcon(new ImageIcon(icon.getImage().getScaledInstance(16, 16, Image.SCALE_SMOOTH)));
    35                 else {
    36                         if (a.types == null)
    37                                 l.setIcon(ImageProvider.get("data", "empty"));
    38                         else if (a.types.size() != 1)
    39                                 l.setIcon(ImageProvider.get("data", "object"));
    40                         else
    41                                 l.setIcon(ImageProvider.get("data", a.types.iterator().next().getSimpleName().toLowerCase()));
    42                 }
    43                 l.setOpaque(true);
    44                 return l;
    45         }
     17    @Override public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
     18        TaggingPreset a = null;
     19        if (value instanceof ForwardActionListener)
     20            a = ((ForwardActionListener)value).preset;
     21        else if (value instanceof TaggingPreset)
     22            a = (TaggingPreset)value;
     23        String name = a == null ? null : (String)a.getValue(Action.NAME);
     24        if (name == null)
     25            return super.getListCellRendererComponent(list, "", index, false, false);
     26        JComponent c = (JComponent)super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
     27        JLabel l = new JLabel(name);
     28        l.setForeground(c.getForeground());
     29        l.setBackground(c.getBackground());
     30        l.setFont(c.getFont());
     31        l.setBorder(c.getBorder());
     32        ImageIcon icon = (ImageIcon)a.getValue(Action.SMALL_ICON);
     33        if (icon != null)
     34            l.setIcon(new ImageIcon(icon.getImage().getScaledInstance(16, 16, Image.SCALE_SMOOTH)));
     35        else {
     36            if (a.types == null)
     37                l.setIcon(ImageProvider.get("data", "empty"));
     38            else if (a.types.size() != 1)
     39                l.setIcon(ImageProvider.get("data", "object"));
     40            else
     41                l.setIcon(ImageProvider.get("data", a.types.iterator().next().getSimpleName().toLowerCase()));
     42        }
     43        l.setOpaque(true);
     44        return l;
     45    }
    4646}
  • trunk/src/org/openstreetmap/josm/gui/tagging/TaggingPreset.java

    r1142 r1169  
    5050 * read in all predefined presets, either shipped with JOSM or that are
    5151 * in the config directory.
    52  * 
     52 *
    5353 * It is also able to construct dialogs out of preset definitions.
    5454 */
    5555public class TaggingPreset extends AbstractAction {
    5656
    57         public TaggingPresetMenu group = null;
    58         public String name;
    59 
    60         public static abstract class Item {
    61                 public boolean focus = false;
    62                 abstract void addToPanel(JPanel p, Collection<OsmPrimitive> sel);
    63                 abstract void addCommands(Collection<OsmPrimitive> sel, List<Command> cmds);
    64                 boolean requestFocusInWindow() {return false;}
    65         }
    66        
    67         public static class Usage {
    68                 Set<String> values;
    69                 Boolean hadKeys = false;
    70                 Boolean hadEmpty = false;
    71                 public Boolean allSimilar()
    72                 {
    73                         return values.size() == 1 && !hadEmpty;
    74                 }
    75                 public Boolean unused()
    76                 {
    77                         return values.size() == 0;
    78                 }
    79                 public String getFirst()
    80                 {
    81                         return (String)(values.toArray()[0]);
    82                 }
    83                 public Boolean hadKeys()
    84                 {
    85                         return hadKeys;
    86                 }
    87         }
    88        
    89         public static final String DIFFERENT = tr("<different>");
    90        
    91         static Usage determineTextUsage(Collection<OsmPrimitive> sel, String key) {
    92                 Usage returnValue = new Usage();
    93                 returnValue.values = new HashSet<String>();
    94                 for (OsmPrimitive s : sel) {
    95                         String v = s.get(key);
    96                         if (v != null)
    97                                 returnValue.values.add(v);
    98                         else
    99                                 returnValue.hadEmpty = true;
    100                         if(s.keys != null && s.keys.size() > 0)
    101                                 returnValue.hadKeys = true;
    102                 }
    103                 return returnValue;
    104         }
    105 
    106         static Usage determineBooleanUsage(Collection<OsmPrimitive> sel, String key) {
    107 
    108                 Usage returnValue = new Usage();
    109                 returnValue.values = new HashSet<String>();
    110                 for (OsmPrimitive s : sel) {
    111                         returnValue.values.add(OsmUtils.getNamedOsmBoolean(s.get(key)));
    112                 }
    113                 return returnValue;
    114         }
    115        
    116         public static class Text extends Item {
    117                
    118                 public String key;
    119                 public String text;
    120                 public String locale_text;
    121                 public String default_;
    122                 public String originalValue;
    123                 public boolean use_last_as_default = false;
    124                 public boolean delete_if_empty = false;
    125 
    126                 private JComponent value;
    127                
    128                 @Override public void addToPanel(JPanel p, Collection<OsmPrimitive> sel) {
    129                        
    130                         // find out if our key is already used in the selection.
    131                         Usage usage = determineTextUsage(sel, key);
    132                         if (usage.unused())
    133                         {
    134                                 value = new JTextField();
    135                                 if (use_last_as_default && lastValue.containsKey(key)) {
    136                                         ((JTextField)value).setText(lastValue.get(key));
    137                                 } else {
    138                                         ((JTextField)value).setText(default_);
    139                                 }
    140                                 originalValue = null;
    141                         } else if (usage.allSimilar()) {
    142                                 // all objects use the same value
    143                                 value = new JTextField();
    144                                 for (String s : usage.values) ((JTextField) value).setText(s);
    145                                 originalValue = ((JTextField)value).getText();
    146                         } else {
    147                                 // the objects have different values
    148                                 value = new JComboBox(usage.values.toArray());
    149                                 ((JComboBox)value).setEditable(true);
    150                                 ((JComboBox)value).getEditor().setItem(DIFFERENT);
    151                                 originalValue = DIFFERENT;
    152                         }
    153                         if(locale_text == null)
    154                                 locale_text = tr(text);
    155                         p.add(new JLabel(locale_text+":"), GBC.std().insets(0,0,10,0));
    156                         p.add(value, GBC.eol().fill(GBC.HORIZONTAL));
    157                 }
    158                
    159                 @Override public void addCommands(Collection<OsmPrimitive> sel, List<Command> cmds) {
    160                        
    161                         // return if unchanged
    162                         String v = (value instanceof JComboBox) ?
    163                                 ((JComboBox)value).getEditor().getItem().toString() :
    164                                 ((JTextField)value).getText();
    165 
    166                         if (use_last_as_default) lastValue.put(key, v);
    167                         if (v.equals(originalValue) || (originalValue == null && v.length() == 0)) return;
    168 
    169                         if (delete_if_empty && v.length() == 0)
    170                                 v = null;
    171                         cmds.add(new ChangePropertyCommand(sel, key, v));
    172                 }
    173                 @Override boolean requestFocusInWindow() {return value.requestFocusInWindow();}
    174         }
    175 
    176         public static class Check extends Item {
    177 
    178                 public String key;
    179                 public String text;
    180                 public String locale_text;
    181                 public boolean default_ = false; // not used!
    182                 public boolean use_last_as_default = false;
    183 
    184                 private QuadStateCheckBox check;
    185                 private QuadStateCheckBox.State initialState;
    186                
    187                 @Override public void addToPanel(JPanel p, Collection<OsmPrimitive> sel) {
    188                        
    189                         // find out if our key is already used in the selection.
    190                         Usage usage = determineBooleanUsage(sel, key);
    191 
    192                         if(locale_text == null)
    193                                 locale_text = tr(text);
    194 
    195                         String oneValue = null;
    196                         for (String s : usage.values) oneValue = s;
    197                         if (usage.values.size() < 2 && (oneValue == null || OsmUtils.trueval.equals(oneValue) || OsmUtils.falseval.equals(oneValue))) {
    198                                 // all selected objects share the same value which is either true or false or unset,
    199                                 // we can display a standard check box.
    200                                 initialState = OsmUtils.trueval.equals(oneValue) ?
    201                                                         QuadStateCheckBox.State.SELECTED :
    202                                                         OsmUtils.falseval.equals(oneValue) ?
    203                                                         QuadStateCheckBox.State.NOT_SELECTED :
    204                                                         QuadStateCheckBox.State.UNSET;
    205                                 check = new QuadStateCheckBox(locale_text, initialState,
    206                                                 new QuadStateCheckBox.State[] {
    207                                                 QuadStateCheckBox.State.SELECTED,
    208                                                 QuadStateCheckBox.State.NOT_SELECTED,
    209                                                 QuadStateCheckBox.State.UNSET });
    210                         } else {
    211                                 // the objects have different values, or one or more objects have something
    212                                 // else than true/false. we display a quad-state check box
    213                                 // in "partial" state.
    214                                 initialState = QuadStateCheckBox.State.PARTIAL;
    215                                 check = new QuadStateCheckBox(locale_text, QuadStateCheckBox.State.PARTIAL,
    216                                                 new QuadStateCheckBox.State[] {
    217                                                 QuadStateCheckBox.State.PARTIAL,
    218                                                 QuadStateCheckBox.State.SELECTED,
    219                                                 QuadStateCheckBox.State.NOT_SELECTED,
    220                                                 QuadStateCheckBox.State.UNSET });
    221                         }
    222                         p.add(check, GBC.eol().fill(GBC.HORIZONTAL));
    223                 }
    224                
    225                 @Override public void addCommands(Collection<OsmPrimitive> sel, List<Command> cmds) {
    226                         // if the user hasn't changed anything, don't create a command.
    227                         if (check.getState() == initialState) return;
    228                        
    229                         // otherwise change things according to the selected value.
    230                         cmds.add(new ChangePropertyCommand(sel, key,
    231                                         check.getState() == QuadStateCheckBox.State.SELECTED ? OsmUtils.trueval :
    232                                         check.getState() == QuadStateCheckBox.State.NOT_SELECTED ? OsmUtils.falseval :
    233                                         null));
    234                 }
    235                 @Override boolean requestFocusInWindow() {return check.requestFocusInWindow();}
    236         }
    237 
    238         public static class Combo extends Item {
    239                
    240                 public String key;
    241                 public String text;
    242                 public String locale_text;
    243                 public String values;
    244                 public String display_values;
    245                 public String locale_display_values;
    246                 public String default_;
    247                 public boolean delete_if_empty = false;
    248                 public boolean editable = true;
    249                 public boolean use_last_as_default = false;
    250 
    251                 private JComboBox combo;
    252                 private LinkedHashMap<String,String> lhm;
    253                 private Usage usage;
    254                 private String originalValue;
    255                
    256                 @Override public void addToPanel(JPanel p, Collection<OsmPrimitive> sel) {
    257                        
    258                         // find out if our key is already used in the selection.
    259                         usage = determineTextUsage(sel, key);
    260                        
    261                         String[] value_array = values.split(",");
    262                         String[] display_array;
    263                         if(locale_display_values != null)
    264                                 display_array = locale_display_values.split(",");
    265                         else if(display_values != null)
    266                                 display_array = display_values.split(",");
    267                         else
    268                                 display_array = value_array;
    269 
    270                         lhm = new LinkedHashMap<String,String>();
    271                         if (!usage.allSimilar() && !usage.unused())
    272                         {
    273                                 lhm.put(DIFFERENT, DIFFERENT);
    274                         }
    275                         for (int i=0; i<value_array.length; i++) {
    276                                 lhm.put(value_array[i],
    277                                 (locale_display_values == null) ?
    278                                 tr(display_array[i]) : display_array[i]);
    279                         }
    280                         if(!usage.unused())
    281                         {
    282                                 for (String s : usage.values) {
    283                                         if (!lhm.containsKey(s)) lhm.put(s, s);
    284                                 }
    285                         }
    286                         if (default_ != null && !lhm.containsKey(default_)) lhm.put(default_, default_);
    287                         if(!lhm.containsKey("")) lhm.put("", "");
    288 
    289                         combo = new JComboBox(lhm.values().toArray());
    290                         combo.setEditable(editable);
    291                         if (usage.allSimilar() && !usage.unused())
    292                         {
    293                                 originalValue=usage.getFirst();
    294                                 combo.setSelectedItem(lhm.get(originalValue));
    295                         }
    296                         // use default only in case it is a totally new entry
    297                         else if(default_ != null && !usage.hadKeys())
    298                         {
    299                                 combo.setSelectedItem(default_);
    300                                 originalValue=DIFFERENT;
    301                         }
    302                         else if(usage.unused())
    303                         {
    304                                 combo.setSelectedItem("");
    305                                 originalValue="";
    306                         }
    307                         else
    308                         {
    309                                 combo.setSelectedItem(DIFFERENT);
    310                                 originalValue=DIFFERENT;
    311                         }
    312 
    313                         if(locale_text == null)
    314                                 locale_text = tr(text);
    315                         p.add(new JLabel(locale_text+":"), GBC.std().insets(0,0,10,0));
    316                         p.add(combo, GBC.eol().fill(GBC.HORIZONTAL));
    317                 }
    318                 @Override public void addCommands(Collection<OsmPrimitive> sel, List<Command> cmds) {
    319                         Object obj = combo.getSelectedItem();
    320                         String display = (obj == null) ? null : obj.toString();
    321                         String value = null;
    322                         if(display == null && combo.isEditable())
    323                                 display = combo.getEditor().getItem().toString();
    324 
    325                         if (display != null)
    326                         {
    327                                 for (String key : lhm.keySet()) {
    328                                         String k = lhm.get(key);
    329                                         if (k != null && k.equals(display)) value=key;
    330                                 }
    331                                 if(value == null)
    332                                         value = display;
    333                         }
    334                         else
    335                                 value = "";
    336 
    337                         // no change if same as before
    338                         if (value.equals(originalValue) || (originalValue == null && (value == null || value.length() == 0))) return;
    339                        
    340                         if (delete_if_empty && value != null && value.length() == 0)
    341                                 value = null;
    342                         cmds.add(new ChangePropertyCommand(sel, key, value));
    343                 }
    344                 @Override boolean requestFocusInWindow() {return combo.requestFocusInWindow();}
    345         }
    346 
    347         public static class Label extends Item {
    348                 public String text;
    349                 public String locale_text;
    350 
    351                 @Override public void addToPanel(JPanel p, Collection<OsmPrimitive> sel) {
    352                         if(locale_text == null)
    353                                 locale_text = tr(text);
    354                         p.add(new JLabel(locale_text), GBC.eol());
    355                 }
    356                 @Override public void addCommands(Collection<OsmPrimitive> sel, List<Command> cmds) {}
    357         }
    358 
    359         public static class Key extends Item {
    360                 public String key;
    361                 public String value;
    362 
    363                 @Override public void addToPanel(JPanel p, Collection<OsmPrimitive> sel) { }
    364                 @Override public void addCommands(Collection<OsmPrimitive> sel, List<Command> cmds) {
    365                         cmds.add(new ChangePropertyCommand(sel, key, value != null && !value.equals("") ? value : null));
    366                 }
    367         }
    368 
    369         /**
    370         * The types as preparsed collection.
    371         */
    372         public Collection<Class<?>> types;
    373         public List<Item> data = new LinkedList<Item>();
    374         private static HashMap<String,String> lastValue = new HashMap<String,String>();
    375 
    376         /**
    377         * Create an empty tagging preset. This will not have any items and
    378         * will be an empty string as text. createPanel will return null.
    379         * Use this as default item for "do not select anything".
    380         */
    381         public TaggingPreset() {}
    382 
    383         /**
    384         * Change the display name without changing the toolbar value.
    385         */
    386         public void setDisplayName() {
    387                 putValue(Action.NAME, getName());
    388                 putValue("toolbar", "tagging_" + getRawName());
    389                 putValue(SHORT_DESCRIPTION, "<html>"+ (group != null ?
    390                 tr("Use preset ''{0}'' of group ''{1}''", tr(name), group.getName()) :
    391                 tr("Use preset ''{0}''", tr(name)))
    392                 +"</html>");
    393         }
    394 
    395         public String getName() {
    396                 return group != null ? group.getName() + "/" + tr(name) : tr(name);
    397         }
    398         public String getRawName() {
    399                 return group != null ? group.getRawName() + "/" + name : name;
    400         }
    401         /**
    402         * Called from the XML parser to set the icon
    403          *
    404         * FIXME for Java 1.6 - use 24x24 icons for LARGE_ICON_KEY (button bar)
    405         * and the 16x16 icons for SMALL_ICON.
    406         */
    407         public void setIcon(String iconName) {
    408                 String s = Main.pref.get("taggingpreset.iconpaths");
    409                 ImageIcon icon = ImageProvider.getIfAvailable((s != null ? s.split(";") : null), "presets", null, iconName);
    410                 if (icon == null)
    411                 {
    412                         System.out.println("Could not get presets icon " + iconName);
    413                         icon = new ImageIcon(iconName);
    414                 }
    415                 if (Math.max(icon.getIconHeight(), icon.getIconWidth()) != 16)
    416                         icon = new ImageIcon(icon.getImage().getScaledInstance(16, 16, Image.SCALE_SMOOTH));
    417                 putValue(Action.SMALL_ICON, icon);
    418         }
    419 
    420         /**
    421         * Called from the XML parser to set the types this preset affects
    422         */
    423         public void setType(String types) throws SAXException {
    424                 try {
    425                         for (String type : types.split(",")) {
    426                                 type = Character.toUpperCase(type.charAt(0))+type.substring(1);
    427                                 if (this.types == null)
    428                                         this.types = new LinkedList<Class<?>>();
    429                                 this.types.add(Class.forName("org.openstreetmap.josm.data.osm."+type));
    430                         }
    431                 } catch (ClassNotFoundException e) {
    432                         e.printStackTrace();
    433                         throw new SAXException(tr("Unknown type"));
    434                 }
    435         }
    436 
    437         public static List<TaggingPreset> readAll(Reader in) throws SAXException {
    438                 XmlObjectParser parser = new XmlObjectParser();
    439                 parser.mapOnStart("item", TaggingPreset.class);
    440                 parser.mapOnStart("separator", TaggingPresetSeparator.class);
    441                 parser.mapBoth("group", TaggingPresetMenu.class);
    442                 parser.map("text", Text.class);
    443                 parser.map("check", Check.class);
    444                 parser.map("combo", Combo.class);
    445                 parser.map("label", Label.class);
    446                 parser.map("key", Key.class);
    447                 LinkedList<TaggingPreset> all = new LinkedList<TaggingPreset>();
    448                 TaggingPresetMenu lastmenu = null;
    449                 parser.start(in);
    450                 while(parser.hasNext()) {
    451                         Object o = parser.next();
    452                         if (o instanceof TaggingPresetMenu) {
    453                                 TaggingPresetMenu tp = (TaggingPresetMenu) o;
    454                                 if(tp == lastmenu)
    455                                         lastmenu = tp.group;
    456                                 else
    457                                 {
    458                                         tp.setDisplayName();
    459                                         tp.group = lastmenu;
    460                                         lastmenu = tp;
    461                                         all.add(tp);
    462                                         Main.toolbar.register(tp);
    463                                        
    464                                 }
    465                         } else if (o instanceof TaggingPresetSeparator) {
    466                                 TaggingPresetSeparator tp = (TaggingPresetSeparator) o;
    467                                 tp.group = lastmenu;
    468                                 all.add(tp);
    469                         } else if (o instanceof TaggingPreset) {
    470                                 TaggingPreset tp = (TaggingPreset) o;
    471                                 tp.group = lastmenu;
    472                                 tp.setDisplayName();
    473                                 all.add(tp);
    474                                 Main.toolbar.register(tp);
    475                         } else
    476                                 all.getLast().data.add((Item)o);
    477                 }
    478                 return all;
    479         }
    480 
    481         public static Collection<TaggingPreset> readFromPreferences() {
    482                 LinkedList<TaggingPreset> allPresets = new LinkedList<TaggingPreset>();
    483                 String allTaggingPresets = Main.pref.get("taggingpreset.sources");
    484                
    485                 if (Main.pref.getBoolean("taggingpreset.enable-defaults", true))
    486                 {
    487                         allTaggingPresets = "resource://presets/presets.xml"
    488                         + (allTaggingPresets != null ? ";"+allTaggingPresets : "");
    489                 }
    490                
    491                 for(String source : allTaggingPresets.split(";"))
    492                 {
    493                         try {
    494                                 MirroredInputStream s = new MirroredInputStream(source);
    495                                 InputStreamReader r;
    496                                 try
    497                                 {
    498                                         r = new InputStreamReader(s, "UTF-8");
    499                                 }
    500                                 catch (UnsupportedEncodingException e)
    501                                 {
    502                                         r = new InputStreamReader(s);
    503                                 }
    504                                 allPresets.addAll(TaggingPreset.readAll(new BufferedReader(r)));
    505                         } catch (IOException e) {
    506                                 e.printStackTrace();
    507                                 JOptionPane.showMessageDialog(Main.parent, tr("Could not read tagging preset source: {0}",source));
    508                         } catch (SAXException e) {
    509                                 e.printStackTrace();
    510                                 JOptionPane.showMessageDialog(Main.parent, tr("Error parsing {0}: ", source)+e.getMessage());
    511                         }
    512                 }
    513                 return allPresets;
    514         }
    515 
    516         public JPanel createPanel(Collection<OsmPrimitive> selected) {
    517                 if (data == null)
    518                         return null;
    519                 JPanel p = new JPanel(new GridBagLayout());
    520 
    521                 for (Item i : data)
    522                         i.addToPanel(p, selected);
    523                 return p;
    524         }
    525 
    526         public void actionPerformed(ActionEvent e) {
    527                 Collection<OsmPrimitive> sel = Main.ds.getSelected();
    528                 JPanel p = createPanel(sel);
    529                 if (p == null)
    530                         return;
    531                 int answer = JOptionPane.OK_OPTION;
    532                 if (p.getComponentCount() != 0) {
    533                         final JOptionPane optionPane = new JOptionPane(p, JOptionPane.PLAIN_MESSAGE, JOptionPane.OK_CANCEL_OPTION){
    534                                 @Override public void selectInitialValue() {
    535                                         for (Item i : data) {
    536                                                 if (i.focus) {
    537                                                         i.requestFocusInWindow();
    538                                                         return;
    539                                                 }
    540                                         }
    541                                 }
    542                         };
    543                         optionPane.createDialog(Main.parent, trn("Change {0} object", "Change {0} objects", sel.size(), sel.size())).setVisible(true);
    544                         Object answerObj = optionPane.getValue();
    545                         if (answerObj == null || answerObj == JOptionPane.UNINITIALIZED_VALUE ||
    546                                         (answerObj instanceof Integer && (Integer)answerObj != JOptionPane.OK_OPTION))
    547                                 answer = JOptionPane.CANCEL_OPTION;
    548                 }
    549                 if (answer == JOptionPane.OK_OPTION) {
    550                         Command cmd = createCommand(Main.ds.getSelected());
    551                         if (cmd != null)
    552                                 Main.main.undoRedo.add(cmd);
    553                 }
    554                 Main.ds.setSelected(Main.ds.getSelected()); // force update
    555         }
    556 
    557         private Command createCommand(Collection<OsmPrimitive> participants) {
    558                 Collection<OsmPrimitive> sel = new LinkedList<OsmPrimitive>();
    559                 for (OsmPrimitive osm : participants)
    560                         if (types == null || types.contains(osm.getClass()))
    561                                 sel.add(osm);
    562                 if (sel.isEmpty())
    563                         return null;
    564 
    565                 List<Command> cmds = new LinkedList<Command>();
    566                 for (Item i : data)
    567                         i.addCommands(sel, cmds);
    568                 if (cmds.size() == 0)
    569                         return null;
    570                 else if (cmds.size() == 1)
    571                         return cmds.get(0);
    572                 else
    573                         return new SequenceCommand(tr("Change Properties"), cmds);
    574         }
     57    public TaggingPresetMenu group = null;
     58    public String name;
     59
     60    public static abstract class Item {
     61        public boolean focus = false;
     62        abstract void addToPanel(JPanel p, Collection<OsmPrimitive> sel);
     63        abstract void addCommands(Collection<OsmPrimitive> sel, List<Command> cmds);
     64        boolean requestFocusInWindow() {return false;}
     65    }
     66
     67    public static class Usage {
     68        Set<String> values;
     69        Boolean hadKeys = false;
     70        Boolean hadEmpty = false;
     71        public Boolean allSimilar()
     72        {
     73            return values.size() == 1 && !hadEmpty;
     74        }
     75        public Boolean unused()
     76        {
     77            return values.size() == 0;
     78        }
     79        public String getFirst()
     80        {
     81            return (String)(values.toArray()[0]);
     82        }
     83        public Boolean hadKeys()
     84        {
     85            return hadKeys;
     86        }
     87    }
     88
     89    public static final String DIFFERENT = tr("<different>");
     90
     91    static Usage determineTextUsage(Collection<OsmPrimitive> sel, String key) {
     92        Usage returnValue = new Usage();
     93        returnValue.values = new HashSet<String>();
     94        for (OsmPrimitive s : sel) {
     95            String v = s.get(key);
     96            if (v != null)
     97                returnValue.values.add(v);
     98            else
     99                returnValue.hadEmpty = true;
     100            if(s.keys != null && s.keys.size() > 0)
     101                returnValue.hadKeys = true;
     102        }
     103        return returnValue;
     104    }
     105
     106    static Usage determineBooleanUsage(Collection<OsmPrimitive> sel, String key) {
     107
     108        Usage returnValue = new Usage();
     109        returnValue.values = new HashSet<String>();
     110        for (OsmPrimitive s : sel) {
     111            returnValue.values.add(OsmUtils.getNamedOsmBoolean(s.get(key)));
     112        }
     113        return returnValue;
     114    }
     115
     116    public static class Text extends Item {
     117
     118        public String key;
     119        public String text;
     120        public String locale_text;
     121        public String default_;
     122        public String originalValue;
     123        public boolean use_last_as_default = false;
     124        public boolean delete_if_empty = false;
     125
     126        private JComponent value;
     127
     128        @Override public void addToPanel(JPanel p, Collection<OsmPrimitive> sel) {
     129
     130            // find out if our key is already used in the selection.
     131            Usage usage = determineTextUsage(sel, key);
     132            if (usage.unused())
     133            {
     134                value = new JTextField();
     135                if (use_last_as_default && lastValue.containsKey(key)) {
     136                    ((JTextField)value).setText(lastValue.get(key));
     137                } else {
     138                    ((JTextField)value).setText(default_);
     139                }
     140                originalValue = null;
     141            } else if (usage.allSimilar()) {
     142                // all objects use the same value
     143                value = new JTextField();
     144                for (String s : usage.values) ((JTextField) value).setText(s);
     145                originalValue = ((JTextField)value).getText();
     146            } else {
     147                // the objects have different values
     148                value = new JComboBox(usage.values.toArray());
     149                ((JComboBox)value).setEditable(true);
     150                ((JComboBox)value).getEditor().setItem(DIFFERENT);
     151                originalValue = DIFFERENT;
     152            }
     153            if(locale_text == null)
     154                locale_text = tr(text);
     155            p.add(new JLabel(locale_text+":"), GBC.std().insets(0,0,10,0));
     156            p.add(value, GBC.eol().fill(GBC.HORIZONTAL));
     157        }
     158
     159        @Override public void addCommands(Collection<OsmPrimitive> sel, List<Command> cmds) {
     160
     161            // return if unchanged
     162            String v = (value instanceof JComboBox) ?
     163                ((JComboBox)value).getEditor().getItem().toString() :
     164                ((JTextField)value).getText();
     165
     166            if (use_last_as_default) lastValue.put(key, v);
     167            if (v.equals(originalValue) || (originalValue == null && v.length() == 0)) return;
     168
     169            if (delete_if_empty && v.length() == 0)
     170                v = null;
     171            cmds.add(new ChangePropertyCommand(sel, key, v));
     172        }
     173        @Override boolean requestFocusInWindow() {return value.requestFocusInWindow();}
     174    }
     175
     176    public static class Check extends Item {
     177
     178        public String key;
     179        public String text;
     180        public String locale_text;
     181        public boolean default_ = false; // not used!
     182        public boolean use_last_as_default = false;
     183
     184        private QuadStateCheckBox check;
     185        private QuadStateCheckBox.State initialState;
     186
     187        @Override public void addToPanel(JPanel p, Collection<OsmPrimitive> sel) {
     188
     189            // find out if our key is already used in the selection.
     190            Usage usage = determineBooleanUsage(sel, key);
     191
     192            if(locale_text == null)
     193                locale_text = tr(text);
     194
     195            String oneValue = null;
     196            for (String s : usage.values) oneValue = s;
     197            if (usage.values.size() < 2 && (oneValue == null || OsmUtils.trueval.equals(oneValue) || OsmUtils.falseval.equals(oneValue))) {
     198                // all selected objects share the same value which is either true or false or unset,
     199                // we can display a standard check box.
     200                initialState = OsmUtils.trueval.equals(oneValue) ?
     201                            QuadStateCheckBox.State.SELECTED :
     202                            OsmUtils.falseval.equals(oneValue) ?
     203                            QuadStateCheckBox.State.NOT_SELECTED :
     204                            QuadStateCheckBox.State.UNSET;
     205                check = new QuadStateCheckBox(locale_text, initialState,
     206                        new QuadStateCheckBox.State[] {
     207                        QuadStateCheckBox.State.SELECTED,
     208                        QuadStateCheckBox.State.NOT_SELECTED,
     209                        QuadStateCheckBox.State.UNSET });
     210            } else {
     211                // the objects have different values, or one or more objects have something
     212                // else than true/false. we display a quad-state check box
     213                // in "partial" state.
     214                initialState = QuadStateCheckBox.State.PARTIAL;
     215                check = new QuadStateCheckBox(locale_text, QuadStateCheckBox.State.PARTIAL,
     216                        new QuadStateCheckBox.State[] {
     217                        QuadStateCheckBox.State.PARTIAL,
     218                        QuadStateCheckBox.State.SELECTED,
     219                        QuadStateCheckBox.State.NOT_SELECTED,
     220                        QuadStateCheckBox.State.UNSET });
     221            }
     222            p.add(check, GBC.eol().fill(GBC.HORIZONTAL));
     223        }
     224
     225        @Override public void addCommands(Collection<OsmPrimitive> sel, List<Command> cmds) {
     226            // if the user hasn't changed anything, don't create a command.
     227            if (check.getState() == initialState) return;
     228
     229            // otherwise change things according to the selected value.
     230            cmds.add(new ChangePropertyCommand(sel, key,
     231                    check.getState() == QuadStateCheckBox.State.SELECTED ? OsmUtils.trueval :
     232                    check.getState() == QuadStateCheckBox.State.NOT_SELECTED ? OsmUtils.falseval :
     233                    null));
     234        }
     235        @Override boolean requestFocusInWindow() {return check.requestFocusInWindow();}
     236    }
     237
     238    public static class Combo extends Item {
     239
     240        public String key;
     241        public String text;
     242        public String locale_text;
     243        public String values;
     244        public String display_values;
     245        public String locale_display_values;
     246        public String default_;
     247        public boolean delete_if_empty = false;
     248        public boolean editable = true;
     249        public boolean use_last_as_default = false;
     250
     251        private JComboBox combo;
     252        private LinkedHashMap<String,String> lhm;
     253        private Usage usage;
     254        private String originalValue;
     255
     256        @Override public void addToPanel(JPanel p, Collection<OsmPrimitive> sel) {
     257
     258            // find out if our key is already used in the selection.
     259            usage = determineTextUsage(sel, key);
     260
     261            String[] value_array = values.split(",");
     262            String[] display_array;
     263            if(locale_display_values != null)
     264                display_array = locale_display_values.split(",");
     265            else if(display_values != null)
     266                display_array = display_values.split(",");
     267            else
     268                display_array = value_array;
     269
     270            lhm = new LinkedHashMap<String,String>();
     271            if (!usage.allSimilar() && !usage.unused())
     272            {
     273                lhm.put(DIFFERENT, DIFFERENT);
     274            }
     275            for (int i=0; i<value_array.length; i++) {
     276                lhm.put(value_array[i],
     277                (locale_display_values == null) ?
     278                tr(display_array[i]) : display_array[i]);
     279            }
     280            if(!usage.unused())
     281            {
     282                for (String s : usage.values) {
     283                    if (!lhm.containsKey(s)) lhm.put(s, s);
     284                }
     285            }
     286            if (default_ != null && !lhm.containsKey(default_)) lhm.put(default_, default_);
     287            if(!lhm.containsKey("")) lhm.put("", "");
     288
     289            combo = new JComboBox(lhm.values().toArray());
     290            combo.setEditable(editable);
     291            if (usage.allSimilar() && !usage.unused())
     292            {
     293                originalValue=usage.getFirst();
     294                combo.setSelectedItem(lhm.get(originalValue));
     295            }
     296            // use default only in case it is a totally new entry
     297            else if(default_ != null && !usage.hadKeys())
     298            {
     299                combo.setSelectedItem(default_);
     300                originalValue=DIFFERENT;
     301            }
     302            else if(usage.unused())
     303            {
     304                combo.setSelectedItem("");
     305                originalValue="";
     306            }
     307            else
     308            {
     309                combo.setSelectedItem(DIFFERENT);
     310                originalValue=DIFFERENT;
     311            }
     312
     313            if(locale_text == null)
     314                locale_text = tr(text);
     315            p.add(new JLabel(locale_text+":"), GBC.std().insets(0,0,10,0));
     316            p.add(combo, GBC.eol().fill(GBC.HORIZONTAL));
     317        }
     318        @Override public void addCommands(Collection<OsmPrimitive> sel, List<Command> cmds) {
     319            Object obj = combo.getSelectedItem();
     320            String display = (obj == null) ? null : obj.toString();
     321            String value = null;
     322            if(display == null && combo.isEditable())
     323                display = combo.getEditor().getItem().toString();
     324
     325            if (display != null)
     326            {
     327                for (String key : lhm.keySet()) {
     328                    String k = lhm.get(key);
     329                    if (k != null && k.equals(display)) value=key;
     330                }
     331                if(value == null)
     332                    value = display;
     333            }
     334            else
     335                value = "";
     336
     337            // no change if same as before
     338            if (value.equals(originalValue) || (originalValue == null && (value == null || value.length() == 0))) return;
     339
     340            if (delete_if_empty && value != null && value.length() == 0)
     341                value = null;
     342            cmds.add(new ChangePropertyCommand(sel, key, value));
     343        }
     344        @Override boolean requestFocusInWindow() {return combo.requestFocusInWindow();}
     345    }
     346
     347    public static class Label extends Item {
     348        public String text;
     349        public String locale_text;
     350
     351        @Override public void addToPanel(JPanel p, Collection<OsmPrimitive> sel) {
     352            if(locale_text == null)
     353                locale_text = tr(text);
     354            p.add(new JLabel(locale_text), GBC.eol());
     355        }
     356        @Override public void addCommands(Collection<OsmPrimitive> sel, List<Command> cmds) {}
     357    }
     358
     359    public static class Key extends Item {
     360        public String key;
     361        public String value;
     362
     363        @Override public void addToPanel(JPanel p, Collection<OsmPrimitive> sel) { }
     364        @Override public void addCommands(Collection<OsmPrimitive> sel, List<Command> cmds) {
     365            cmds.add(new ChangePropertyCommand(sel, key, value != null && !value.equals("") ? value : null));
     366        }
     367    }
     368
     369    /**
     370    * The types as preparsed collection.
     371    */
     372    public Collection<Class<?>> types;
     373    public List<Item> data = new LinkedList<Item>();
     374    private static HashMap<String,String> lastValue = new HashMap<String,String>();
     375
     376    /**
     377    * Create an empty tagging preset. This will not have any items and
     378    * will be an empty string as text. createPanel will return null.
     379    * Use this as default item for "do not select anything".
     380    */
     381    public TaggingPreset() {}
     382
     383    /**
     384    * Change the display name without changing the toolbar value.
     385    */
     386    public void setDisplayName() {
     387        putValue(Action.NAME, getName());
     388        putValue("toolbar", "tagging_" + getRawName());
     389        putValue(SHORT_DESCRIPTION, "<html>"+ (group != null ?
     390        tr("Use preset ''{0}'' of group ''{1}''", tr(name), group.getName()) :
     391        tr("Use preset ''{0}''", tr(name)))
     392        +"</html>");
     393    }
     394
     395    public String getName() {
     396        return group != null ? group.getName() + "/" + tr(name) : tr(name);
     397    }
     398    public String getRawName() {
     399        return group != null ? group.getRawName() + "/" + name : name;
     400    }
     401    /**
     402    * Called from the XML parser to set the icon
     403     *
     404    * FIXME for Java 1.6 - use 24x24 icons for LARGE_ICON_KEY (button bar)
     405    * and the 16x16 icons for SMALL_ICON.
     406    */
     407    public void setIcon(String iconName) {
     408        String s = Main.pref.get("taggingpreset.iconpaths");
     409        ImageIcon icon = ImageProvider.getIfAvailable((s != null ? s.split(";") : null), "presets", null, iconName);
     410        if (icon == null)
     411        {
     412            System.out.println("Could not get presets icon " + iconName);
     413            icon = new ImageIcon(iconName);
     414        }
     415        if (Math.max(icon.getIconHeight(), icon.getIconWidth()) != 16)
     416            icon = new ImageIcon(icon.getImage().getScaledInstance(16, 16, Image.SCALE_SMOOTH));
     417        putValue(Action.SMALL_ICON, icon);
     418    }
     419
     420    /**
     421    * Called from the XML parser to set the types this preset affects
     422    */
     423    public void setType(String types) throws SAXException {
     424        try {
     425            for (String type : types.split(",")) {
     426                type = Character.toUpperCase(type.charAt(0))+type.substring(1);
     427                if (this.types == null)
     428                    this.types = new LinkedList<Class<?>>();
     429                this.types.add(Class.forName("org.openstreetmap.josm.data.osm."+type));
     430            }
     431        } catch (ClassNotFoundException e) {
     432            e.printStackTrace();
     433            throw new SAXException(tr("Unknown type"));
     434        }
     435    }
     436
     437    public static List<TaggingPreset> readAll(Reader in) throws SAXException {
     438        XmlObjectParser parser = new XmlObjectParser();
     439        parser.mapOnStart("item", TaggingPreset.class);
     440        parser.mapOnStart("separator", TaggingPresetSeparator.class);
     441        parser.mapBoth("group", TaggingPresetMenu.class);
     442        parser.map("text", Text.class);
     443        parser.map("check", Check.class);
     444        parser.map("combo", Combo.class);
     445        parser.map("label", Label.class);
     446        parser.map("key", Key.class);
     447        LinkedList<TaggingPreset> all = new LinkedList<TaggingPreset>();
     448        TaggingPresetMenu lastmenu = null;
     449        parser.start(in);
     450        while(parser.hasNext()) {
     451            Object o = parser.next();
     452            if (o instanceof TaggingPresetMenu) {
     453                TaggingPresetMenu tp = (TaggingPresetMenu) o;
     454                if(tp == lastmenu)
     455                    lastmenu = tp.group;
     456                else
     457                {
     458                    tp.setDisplayName();
     459                    tp.group = lastmenu;
     460                    lastmenu = tp;
     461                    all.add(tp);
     462                    Main.toolbar.register(tp);
     463
     464                }
     465            } else if (o instanceof TaggingPresetSeparator) {
     466                TaggingPresetSeparator tp = (TaggingPresetSeparator) o;
     467                tp.group = lastmenu;
     468                all.add(tp);
     469            } else if (o instanceof TaggingPreset) {
     470                TaggingPreset tp = (TaggingPreset) o;
     471                tp.group = lastmenu;
     472                tp.setDisplayName();
     473                all.add(tp);
     474                Main.toolbar.register(tp);
     475            } else
     476                all.getLast().data.add((Item)o);
     477        }
     478        return all;
     479    }
     480
     481    public static Collection<TaggingPreset> readFromPreferences() {
     482        LinkedList<TaggingPreset> allPresets = new LinkedList<TaggingPreset>();
     483        String allTaggingPresets = Main.pref.get("taggingpreset.sources");
     484
     485        if (Main.pref.getBoolean("taggingpreset.enable-defaults", true))
     486        {
     487            allTaggingPresets = "resource://presets/presets.xml"
     488            + (allTaggingPresets != null ? ";"+allTaggingPresets : "");
     489        }
     490
     491        for(String source : allTaggingPresets.split(";"))
     492        {
     493            try {
     494                MirroredInputStream s = new MirroredInputStream(source);
     495                InputStreamReader r;
     496                try
     497                {
     498                    r = new InputStreamReader(s, "UTF-8");
     499                }
     500                catch (UnsupportedEncodingException e)
     501                {
     502                    r = new InputStreamReader(s);
     503                }
     504                allPresets.addAll(TaggingPreset.readAll(new BufferedReader(r)));
     505            } catch (IOException e) {
     506                e.printStackTrace();
     507                JOptionPane.showMessageDialog(Main.parent, tr("Could not read tagging preset source: {0}",source));
     508            } catch (SAXException e) {
     509                e.printStackTrace();
     510                JOptionPane.showMessageDialog(Main.parent, tr("Error parsing {0}: ", source)+e.getMessage());
     511            }
     512        }
     513        return allPresets;
     514    }
     515
     516    public JPanel createPanel(Collection<OsmPrimitive> selected) {
     517        if (data == null)
     518            return null;
     519        JPanel p = new JPanel(new GridBagLayout());
     520
     521        for (Item i : data)
     522            i.addToPanel(p, selected);
     523        return p;
     524    }
     525
     526    public void actionPerformed(ActionEvent e) {
     527        Collection<OsmPrimitive> sel = Main.ds.getSelected();
     528        JPanel p = createPanel(sel);
     529        if (p == null)
     530            return;
     531        int answer = JOptionPane.OK_OPTION;
     532        if (p.getComponentCount() != 0) {
     533            final JOptionPane optionPane = new JOptionPane(p, JOptionPane.PLAIN_MESSAGE, JOptionPane.OK_CANCEL_OPTION){
     534                @Override public void selectInitialValue() {
     535                    for (Item i : data) {
     536                        if (i.focus) {
     537                            i.requestFocusInWindow();
     538                            return;
     539                        }
     540                    }
     541                }
     542            };
     543            optionPane.createDialog(Main.parent, trn("Change {0} object", "Change {0} objects", sel.size(), sel.size())).setVisible(true);
     544            Object answerObj = optionPane.getValue();
     545            if (answerObj == null || answerObj == JOptionPane.UNINITIALIZED_VALUE ||
     546                    (answerObj instanceof Integer && (Integer)answerObj != JOptionPane.OK_OPTION))
     547                answer = JOptionPane.CANCEL_OPTION;
     548        }
     549        if (answer == JOptionPane.OK_OPTION) {
     550            Command cmd = createCommand(Main.ds.getSelected());
     551            if (cmd != null)
     552                Main.main.undoRedo.add(cmd);
     553        }
     554        Main.ds.setSelected(Main.ds.getSelected()); // force update
     555    }
     556
     557    private Command createCommand(Collection<OsmPrimitive> participants) {
     558        Collection<OsmPrimitive> sel = new LinkedList<OsmPrimitive>();
     559        for (OsmPrimitive osm : participants)
     560            if (types == null || types.contains(osm.getClass()))
     561                sel.add(osm);
     562        if (sel.isEmpty())
     563            return null;
     564
     565        List<Command> cmds = new LinkedList<Command>();
     566        for (Item i : data)
     567            i.addCommands(sel, cmds);
     568        if (cmds.size() == 0)
     569            return null;
     570        else if (cmds.size() == 1)
     571            return cmds.get(0);
     572        else
     573            return new SequenceCommand(tr("Change Properties"), cmds);
     574    }
    575575}
  • trunk/src/org/openstreetmap/josm/gui/tagging/TaggingPresetMenu.java

    r878 r1169  
    1616
    1717public class TaggingPresetMenu extends TaggingPreset {
    18         public JMenu menu = null; // set by TaggingPresetPreferences
    19         public void setDisplayName() {
    20                 String n = getName();
    21                 putValue(Action.NAME, n);
    22                 putValue(SHORT_DESCRIPTION, "<html>"+tr("Preset group ''{0}''", n)+"</html>");
    23                 putValue("toolbar", "tagginggroup_" + getRawName());
    24         }
    25         public void setIcon(String iconName) {
    26                 super.setIcon(iconName);
    27         }
    28         public void actionPerformed(ActionEvent e) {
    29                 Object s = e.getSource();
    30                 if(menu != null && s instanceof Component)
    31                 {
    32                         Component co = (Component)s;
    33                         JPopupMenu pm = new JPopupMenu(getName());
    34                         for(Component c : menu.getMenuComponents())
    35                         {
    36                                 if(c instanceof JMenuItem)
    37                                 {
    38                                         JMenuItem j = new JMenuItem(((JMenuItem)c).getAction());
    39                                         j.setText(((JMenuItem)c).getText());
    40                                         pm.add(j);
    41                                 }
    42                                 else if(c instanceof JSeparator)
    43                                         pm.addSeparator();
    44                         }
    45                         pm.show(co, co.getWidth()/2, co.getHeight()/2);
    46                 }
    47         }
     18    public JMenu menu = null; // set by TaggingPresetPreferences
     19    public void setDisplayName() {
     20        String n = getName();
     21        putValue(Action.NAME, n);
     22        putValue(SHORT_DESCRIPTION, "<html>"+tr("Preset group ''{0}''", n)+"</html>");
     23        putValue("toolbar", "tagginggroup_" + getRawName());
     24    }
     25    public void setIcon(String iconName) {
     26        super.setIcon(iconName);
     27    }
     28    public void actionPerformed(ActionEvent e) {
     29        Object s = e.getSource();
     30        if(menu != null && s instanceof Component)
     31        {
     32            Component co = (Component)s;
     33            JPopupMenu pm = new JPopupMenu(getName());
     34            for(Component c : menu.getMenuComponents())
     35            {
     36                if(c instanceof JMenuItem)
     37                {
     38                    JMenuItem j = new JMenuItem(((JMenuItem)c).getAction());
     39                    j.setText(((JMenuItem)c).getText());
     40                    pm.add(j);
     41                }
     42                else if(c instanceof JSeparator)
     43                    pm.addSeparator();
     44            }
     45            pm.show(co, co.getWidth()/2, co.getHeight()/2);
     46        }
     47    }
    4848}
  • trunk/src/org/openstreetmap/josm/gui/tagging/TaggingPresetSeparator.java

    r895 r1169  
    55
    66public class TaggingPresetSeparator extends TaggingPreset {
    7         public void setDisplayName() {}
     7    public void setDisplayName() {}
    88}
  • trunk/src/org/openstreetmap/josm/io/BoundingBoxDownloader.java

    r999 r1169  
    1515public class BoundingBoxDownloader extends OsmServerReader {
    1616
    17         /**
    18         * The boundings of the desired map data.
    19         */
    20         private final double lat1;
    21         private final double lon1;
    22         private final double lat2;
    23         private final double lon2;
     17    /**
     18    * The boundings of the desired map data.
     19    */
     20    private final double lat1;
     21    private final double lon1;
     22    private final double lat2;
     23    private final double lon2;
    2424
    25         public BoundingBoxDownloader(double lat1, double lon1, double lat2, double lon2) {
    26                 this.lat1 = lat1;
    27                 this.lon1 = lon1;
    28                 this.lat2 = lat2;
    29                 this.lon2 = lon2;
    30                 // store the bounding box in the preferences so it can be
    31                 // re-used across invocations of josm
    32                 Main.pref.put("osm-download.bounds", lat1+";"+lon1+";"+lat2+";"+lon2);
    33         }
     25    public BoundingBoxDownloader(double lat1, double lon1, double lat2, double lon2) {
     26        this.lat1 = lat1;
     27        this.lon1 = lon1;
     28        this.lat2 = lat2;
     29        this.lon2 = lon2;
     30        // store the bounding box in the preferences so it can be
     31        // re-used across invocations of josm
     32        Main.pref.put("osm-download.bounds", lat1+";"+lon1+";"+lat2+";"+lon2);
     33    }
    3434
    35         /**
    36         * Retrieve raw gps waypoints from the server API.
    37         * @return A list of all primitives retrieved. Currently, the list of lists
    38         *      contain only one list, since the server cannot distinguish between
    39         *      ways.
    40         */
    41         public GpxData parseRawGps() throws IOException, SAXException {
    42                 Main.pleaseWaitDlg.progress.setValue(0);
    43                 Main.pleaseWaitDlg.currentAction.setText(tr("Contacting OSM Server..."));
    44                 try {
    45                         String url = "trackpoints?bbox="+lon1+","+lat1+","+lon2+","+lat2+"&page=";
     35    /**
     36    * Retrieve raw gps waypoints from the server API.
     37    * @return A list of all primitives retrieved. Currently, the list of lists
     38    *      contain only one list, since the server cannot distinguish between
     39    *      ways.
     40    */
     41    public GpxData parseRawGps() throws IOException, SAXException {
     42        Main.pleaseWaitDlg.progress.setValue(0);
     43        Main.pleaseWaitDlg.currentAction.setText(tr("Contacting OSM Server..."));
     44        try {
     45            String url = "trackpoints?bbox="+lon1+","+lat1+","+lon2+","+lat2+"&page=";
    4646
    47                         boolean done = false;
    48                         GpxData result = null;
    49                         for (int i = 0;!done;++i) {
    50                                 Main.pleaseWaitDlg.currentAction.setText(tr("Downloading points {0} to {1}...", i * 5000, ((i + 1) * 5000)));
    51                                 InputStream in = getInputStream(url+i, Main.pleaseWaitDlg);
    52                                 if (in == null)
    53                                         break;
    54                                 GpxData currentGpx = new GpxReader(in, null).data;
    55                                 if (result == null) {
    56                                         result = currentGpx;
    57                                 } else if (currentGpx.hasTrackPoints()) {
    58                                         result.mergeFrom(currentGpx);
    59                                 } else{
    60                                         done = true;
    61                                 }
    62                                 in.close();
    63                                 activeConnection = null;
    64                         }
    65                         result.fromServer = true;
    66                         return result;
    67                 } catch (IllegalArgumentException e) {
    68                         // caused by HttpUrlConnection in case of illegal stuff in the response
    69                         if (cancel)
    70                                 return null;
    71                         throw new SAXException("Illegal characters within the HTTP-header response", e);
    72                 } catch (IOException e) {
    73                         if (cancel)
    74                                 return null;
    75                         throw e;
    76                 } catch (SAXException e) {
    77                         throw e;
    78                 } catch (Exception e) {
    79                         if (cancel)
    80                                 return null;
    81                         if (e instanceof RuntimeException)
    82                                 throw (RuntimeException)e;
    83                         throw new RuntimeException(e);
    84                 }
    85         }
     47            boolean done = false;
     48            GpxData result = null;
     49            for (int i = 0;!done;++i) {
     50                Main.pleaseWaitDlg.currentAction.setText(tr("Downloading points {0} to {1}...", i * 5000, ((i + 1) * 5000)));
     51                InputStream in = getInputStream(url+i, Main.pleaseWaitDlg);
     52                if (in == null)
     53                    break;
     54                GpxData currentGpx = new GpxReader(in, null).data;
     55                if (result == null) {
     56                    result = currentGpx;
     57                } else if (currentGpx.hasTrackPoints()) {
     58                    result.mergeFrom(currentGpx);
     59                } else{
     60                    done = true;
     61                }
     62                in.close();
     63                activeConnection = null;
     64            }
     65            result.fromServer = true;
     66            return result;
     67        } catch (IllegalArgumentException e) {
     68            // caused by HttpUrlConnection in case of illegal stuff in the response
     69            if (cancel)
     70                return null;
     71            throw new SAXException("Illegal characters within the HTTP-header response", e);
     72        } catch (IOException e) {
     73            if (cancel)
     74                return null;
     75            throw e;
     76        } catch (SAXException e) {
     77            throw e;
     78        } catch (Exception e) {
     79            if (cancel)
     80                return null;
     81            if (e instanceof RuntimeException)
     82                throw (RuntimeException)e;
     83            throw new RuntimeException(e);
     84        }
     85    }
    8686
    87         /**
    88         * Read the data from the osm server address.
    89         * @return A data set containing all data retrieved from that url
    90         */
    91         public DataSet parseOsm() throws SAXException, IOException {
    92                 try {
    93                         Main.pleaseWaitDlg.progress.setValue(0);
    94                         Main.pleaseWaitDlg.currentAction.setText(tr("Contacting OSM Server..."));
    95                         final InputStream in = getInputStream("map?bbox="+lon1+","+lat1+","+lon2+","+lat2, Main.pleaseWaitDlg);
    96                         if (in == null)
    97                                 return null;
    98                         Main.pleaseWaitDlg.currentAction.setText(tr("Downloading OSM data..."));
    99                         final DataSet data = OsmReader.parseDataSet(in, null, Main.pleaseWaitDlg);
    100                         in.close();
    101                         activeConnection = null;
    102                         return data;
    103                 } catch (IOException e) {
    104                         if (cancel)
    105                                 return null;
    106                         throw e;
    107                 } catch (SAXException e) {
    108                         throw e;
    109                 } catch (Exception e) {
    110                         if (cancel)
    111                                 return null;
    112                         if (e instanceof RuntimeException)
    113                                 throw (RuntimeException)e;
    114                         throw new RuntimeException(e);
    115                 }
    116         }
     87    /**
     88    * Read the data from the osm server address.
     89    * @return A data set containing all data retrieved from that url
     90    */
     91    public DataSet parseOsm() throws SAXException, IOException {
     92        try {
     93            Main.pleaseWaitDlg.progress.setValue(0);
     94            Main.pleaseWaitDlg.currentAction.setText(tr("Contacting OSM Server..."));
     95            final InputStream in = getInputStream("map?bbox="+lon1+","+lat1+","+lon2+","+lat2, Main.pleaseWaitDlg);
     96            if (in == null)
     97                return null;
     98            Main.pleaseWaitDlg.currentAction.setText(tr("Downloading OSM data..."));
     99            final DataSet data = OsmReader.parseDataSet(in, null, Main.pleaseWaitDlg);
     100            in.close();
     101            activeConnection = null;
     102            return data;
     103        } catch (IOException e) {
     104            if (cancel)
     105                return null;
     106            throw e;
     107        } catch (SAXException e) {
     108            throw e;
     109        } catch (Exception e) {
     110            if (cancel)
     111                return null;
     112            if (e instanceof RuntimeException)
     113                throw (RuntimeException)e;
     114            throw new RuntimeException(e);
     115        }
     116    }
    117117}
  • trunk/src/org/openstreetmap/josm/io/DiffResultReader.java

    r1071 r1169  
    4141 */
    4242public class DiffResultReader implements Visitor {
    43    
     43
    4444    /**
    4545     * mapping from old id to new id/version
     
    4747    private Map<String, Long[]> versions = new HashMap<String, Long[]>();
    4848    private Collection<OsmPrimitive> processed;
    49     private Map<OsmPrimitive,Long> newIdMap; 
     49    private Map<OsmPrimitive,Long> newIdMap;
    5050
    51     /** 
     51    /**
    5252     * List of protocol versions that will be accepted on reading
    5353     */
    54    
     54
    5555    private class Parser extends DefaultHandler {
    5656
     
    8181     *  elemet found there is returned.
    8282     */
    83     public static void parseDiffResult(InputStream source, Collection<OsmPrimitive> osm, Collection<OsmPrimitive> processed, Map<OsmPrimitive,Long> newIdMap, PleaseWaitDialog pleaseWaitDlg) 
     83    public static void parseDiffResult(InputStream source, Collection<OsmPrimitive> osm, Collection<OsmPrimitive> processed, Map<OsmPrimitive,Long> newIdMap, PleaseWaitDialog pleaseWaitDlg)
    8484    throws SAXException, IOException {
    8585
     
    107107       }
    108108    }
    109    
     109
    110110    public void visit(Node n) {
    111111        String key = "node:" + (newIdMap.containsKey(n) ? newIdMap.get(n) : n.id);
     
    127127                w.id = nv[0]; w.version = nv[1].intValue();
    128128            }
    129         }       
     129        }
    130130    }
    131131    public void visit(Relation r) {
     
    137137                r.id = nv[0]; r.version = nv[1].intValue();
    138138            }
    139         }   
     139        }
    140140    }
    141141}
  • trunk/src/org/openstreetmap/josm/io/GpxReader.java

    r1122 r1169  
    3535 */
    3636public class GpxReader {
    37         // TODO: implement GPX 1.0 parsing
    38 
    39         /**
    40         * The resulting gpx data
    41         */
    42         public GpxData data;
    43         public enum state { init, metadata, wpt, rte, trk, ext, author, link, trkseg }
    44 
    45         private class Parser extends DefaultHandler {
    46 
    47                 private GpxData currentData;
    48                 private GpxTrack currentTrack;
    49                 private Collection<WayPoint> currentTrackSeg;
    50                 private GpxRoute currentRoute;
    51                 private WayPoint currentWayPoint;
    52 
    53                 private state currentState = state.init;
    54        
    55                 private GpxLink currentLink;
    56                 private Stack<state> states;
    57 
    58                 private StringBuffer accumulator = new StringBuffer();
    59 
    60                 @Override public void startDocument() {
    61                         accumulator = new StringBuffer();
    62                         states = new Stack<state>();
    63                         currentData = new GpxData();
    64                 }               
    65 
    66                 private double parseCoord(String s) {
    67                         try {
    68                                 return Double.parseDouble(s);
    69                         } catch (NumberFormatException ex) {
    70                                 return Double.NaN;
    71                         }
    72                 }
    73 
    74                 private LatLon parseLatLon(Attributes atts) {
    75                         return new LatLon(
    76                                 parseCoord(atts.getValue("lat")),
    77                                 parseCoord(atts.getValue("lon")));
    78                 }
    79 
    80                 @Override public void startElement(String namespaceURI, String qName, String rqName, Attributes atts) throws SAXException {
    81                         switch(currentState) {
    82                         case init:
    83                                 if (qName.equals("metadata")) {
    84                                         states.push(currentState);
    85                                         currentState = state.metadata;
    86                                 } else if (qName.equals("wpt")) {
    87                                         states.push(currentState);
    88                                         currentState = state.wpt;
    89                                         currentWayPoint = new WayPoint(parseLatLon(atts));
    90                                 } else if (qName.equals("rte")) {
    91                                         states.push(currentState);
    92                                         currentState = state.rte;
    93                                         currentRoute = new GpxRoute();
    94                                 } else if (qName.equals("trk")) {
    95                                         states.push(currentState);
    96                                         currentState = state.trk;
    97                                         currentTrack = new GpxTrack();
    98                                 } else if (qName.equals("extensions")) {
    99                                         states.push(currentState);
    100                                         currentState = state.ext;
    101                                 }
    102                                 break;
    103                         case author:
    104                                 if (qName.equals("link")) {
    105                                         states.push(currentState);
    106                                         currentState = state.link;
    107                                         currentLink = new GpxLink(atts.getValue("href"));
    108                                 }
    109                                 break;
    110                         case trk:
    111                                 if (qName.equals("trkseg")) {
    112                                         states.push(currentState);
    113                                         currentState = state.trkseg;
    114                                         currentTrackSeg = new ArrayList<WayPoint>();
    115                                 } else if (qName.equals("link")) {
    116                                         states.push(currentState);
    117                                         currentState = state.link;
    118                                         currentLink = new GpxLink(atts.getValue("href"));
    119                                 } else if (qName.equals("extensions")) {
    120                                         states.push(currentState);
    121                                         currentState = state.ext;
    122                                 }
    123                                 break;
    124                         case metadata:
    125                                 if (qName.equals("author")) {
    126                                         states.push(currentState);
    127                                         currentState = state.author;
    128                                 } else if (qName.equals("extensions")) {
    129                                         states.push(currentState);
    130                                         currentState = state.ext;
    131                                 }
    132                                 break;
    133                         case trkseg:
    134                                 if (qName.equals("trkpt")) {
    135                                         states.push(currentState);
    136                                         currentState = state.wpt;
    137                                         currentWayPoint = new WayPoint(parseLatLon(atts));
    138                                 }
    139                                 break;
    140                         case wpt:
    141                                 if (qName.equals("link")) {
    142                                         states.push(currentState);
    143                                         currentState = state.link;
    144                                         currentLink = new GpxLink(atts.getValue("href"));
    145                                 } else if (qName.equals("extensions")) {
    146                                         states.push(currentState);
    147                                         currentState = state.ext;
    148                                 }
    149                                 break;
    150                         case rte:
    151                                 if (qName.equals("link")) {
    152                                         states.push(currentState);
    153                                         currentState = state.link;
    154                                         currentLink = new GpxLink(atts.getValue("href"));
    155                                 } else if (qName.equals("rtept")) {
    156                                         states.push(currentState);
    157                                         currentState = state.wpt;
    158                                         currentWayPoint = new WayPoint(parseLatLon(atts));
    159                                 } else if (qName.equals("extensions")) {
    160                                         states.push(currentState);
    161                                         currentState = state.ext;
    162                                 }
    163                                 break;
    164                         default:
    165                         }
    166                         accumulator.setLength(0);
    167                 }
    168 
    169                 @Override public void characters(char[] ch, int start, int length) {
    170                         accumulator.append(ch, start, length);
    171                 }
    172 
    173                 private Map<String, Object> getAttr() {
    174                         switch (currentState) {
    175                                 case rte: return currentRoute.attr;
    176                                 case metadata: return currentData.attr;
    177                                 case wpt: return currentWayPoint.attr;
    178                                 case trk: return currentTrack.attr;
    179                                 default: return null;
    180                         }
    181                 }
    182 
    183                 @Override public void endElement(String namespaceURI, String qName, String rqName) {
    184                         switch (currentState) {
    185                         case metadata:
    186                                 if (qName.equals("name") || qName.equals("desc") ||
    187                                                 qName.equals("time") || qName.equals("keywords")) {
    188                                         currentData.attr.put(qName, accumulator.toString());
    189                                 } else if (qName.equals("metadata")) {
    190                                         currentState = states.pop();
    191                                 }
    192                                 //TODO: parse copyright, bounds, extensions
    193                                 break;
    194                         case author:
    195                                 if (qName.equals("author")) {
    196                                         currentState = states.pop();
    197                                 } else if (qName.equals("name") || qName.equals("email")) {
    198                                         currentData.attr.put("author" + qName, accumulator.toString());
    199                                 } else if (qName.equals("link")) {
    200                                         currentData.attr.put("authorlink", currentLink);
    201                                 }
    202                                 break;
    203                         case link:
    204                                 if (qName.equals("text")) {
    205                                         currentLink.text = accumulator.toString();
    206                                 } else if (qName.equals("type")) {
    207                                         currentLink.type = accumulator.toString();
    208                                 } else if (qName.equals("link")) {
    209                                         // <link>URL</link>
    210                                         if (currentLink.uri == null)
    211                                                 currentLink.uri = accumulator.toString();
    212 
    213                                         currentState = states.pop();
    214                                 }
    215                                 if (currentState == state.author) {
    216                                         currentData.attr.put("authorlink", currentLink);
    217                                 } else if (currentState != state.link) {
    218                                         Map<String, Object> attr = getAttr();
    219                                         if (!attr.containsKey("link")) {
    220                                                 attr.put("link", new LinkedList<GpxLink>());
    221                                         }
    222                                         ((Collection<GpxLink>) attr.get("link")).add(currentLink);
    223                                 }
    224                                 break;
    225                         case wpt:
    226                                 if (qName.equals("ele") || qName.equals("magvar")
    227                                                 || qName.equals("geoidheight") || qName.equals("name")
    228                                                 || qName.equals("sym") || qName.equals("type")) {
    229                                         currentWayPoint.attr.put(qName, accumulator.toString());
    230                                 } else if (qName.equals("time")) {
    231                                         currentWayPoint.attr.put(qName, accumulator.toString());
    232                                         currentWayPoint.setTime();                                     
    233                                 } else if (qName.equals("cmt") || qName.equals("desc")) {
    234                                         currentWayPoint.attr.put(qName, accumulator.toString());
    235                                         currentWayPoint.setGarminCommentTime(qName);                                   
    236                                 } else if (qName.equals("rtept")) {
    237                                         currentState = states.pop();
    238                                         currentRoute.routePoints.add(currentWayPoint);
    239                                 } else if (qName.equals("trkpt")) {
    240                                         currentState = states.pop();
    241                                         currentTrackSeg.add(currentWayPoint);
    242                                 } else if (qName.equals("wpt")) {
    243                                         currentState = states.pop();
    244                                         currentData.waypoints.add(currentWayPoint);
    245                                 }
    246                                 break;
    247                         case trkseg:
    248                                 if (qName.equals("trkseg")) {
    249                                         currentState = states.pop();
    250                                         currentTrack.trackSegs.add(currentTrackSeg);
    251                                 }
    252                                 break;
    253                         case trk:
    254                                 if (qName.equals("trk")) {
    255                                         currentState = states.pop();
    256                                         currentData.tracks.add(currentTrack);
    257                                 } else if (qName.equals("name") || qName.equals("cmt")
    258                                                 || qName.equals("desc") || qName.equals("src")
    259                                                 || qName.equals("type") || qName.equals("number")) {
    260                                         currentTrack.attr.put(qName, accumulator.toString());
    261                                 }
    262                                 break;
    263                         case ext:
    264                                 if (qName.equals("extensions")) {
    265                                         currentState = states.pop();
    266                                 }
    267                                 break;
    268                         default:
    269                                 if (qName.equals("wpt")) {
    270                                         currentState = states.pop();
    271                                 } else if (qName.equals("rte")) {
    272                                         currentState = states.pop();
    273                                         currentData.routes.add(currentRoute);
    274                                 }
    275                         }
    276                 }
    277 
    278                 @Override public void endDocument() throws SAXException  {
    279                         if (!states.empty()) {
    280                                 throw new SAXException(tr("Parse error: invalid document structure for gpx document"));
    281                         }
    282                         data = currentData;
    283                 }
    284         }
    285 
    286         /**
    287         * Parse the input stream and store the result in trackData and markerData
    288          *
    289          * @param relativeMarkerPath The directory to use as relative path for all &lt;wpt&gt;
    290          *    marker tags. Maybe <code>null</code>, in which case no relative urls are constructed for the markers.
    291         */
    292         public GpxReader(InputStream source, File relativeMarkerPath) throws SAXException, IOException {
    293                
    294                 Parser parser = new Parser();
    295                 InputSource inputSource = new InputSource(new InputStreamReader(source, "UTF-8"));
    296                 try {
    297                         SAXParserFactory factory = SAXParserFactory.newInstance();
    298                         factory.setNamespaceAware(true);
    299                         factory.newSAXParser().parse(inputSource, parser);
    300                 } catch (ParserConfigurationException e) {
    301                         e.printStackTrace(); // broken SAXException chaining
    302                         throw new SAXException(e);
    303                 }
    304         }
     37    // TODO: implement GPX 1.0 parsing
     38
     39    /**
     40    * The resulting gpx data
     41    */
     42    public GpxData data;
     43    public enum state { init, metadata, wpt, rte, trk, ext, author, link, trkseg }
     44
     45    private class Parser extends DefaultHandler {
     46
     47        private GpxData currentData;
     48        private GpxTrack currentTrack;
     49        private Collection<WayPoint> currentTrackSeg;
     50        private GpxRoute currentRoute;
     51        private WayPoint currentWayPoint;
     52
     53        private state currentState = state.init;
     54
     55        private GpxLink currentLink;
     56        private Stack<state> states;
     57
     58        private StringBuffer accumulator = new StringBuffer();
     59
     60        @Override public void startDocument() {
     61            accumulator = new StringBuffer();
     62            states = new Stack<state>();
     63            currentData = new GpxData();
     64        }
     65
     66        private double parseCoord(String s) {
     67            try {
     68                return Double.parseDouble(s);
     69            } catch (NumberFormatException ex) {
     70                return Double.NaN;
     71            }
     72        }
     73
     74        private LatLon parseLatLon(Attributes atts) {
     75            return new LatLon(
     76                parseCoord(atts.getValue("lat")),
     77                parseCoord(atts.getValue("lon")));
     78        }
     79
     80        @Override public void startElement(String namespaceURI, String qName, String rqName, Attributes atts) throws SAXException {
     81            switch(currentState) {
     82            case init:
     83                if (qName.equals("metadata")) {
     84                    states.push(currentState);
     85                    currentState = state.metadata;
     86                } else if (qName.equals("wpt")) {
     87                    states.push(currentState);
     88                    currentState = state.wpt;
     89                    currentWayPoint = new WayPoint(parseLatLon(atts));
     90                } else if (qName.equals("rte")) {
     91                    states.push(currentState);
     92                    currentState = state.rte;
     93                    currentRoute = new GpxRoute();
     94                } else if (qName.equals("trk")) {
     95                    states.push(currentState);
     96                    currentState = state.trk;
     97                    currentTrack = new GpxTrack();
     98                } else if (qName.equals("extensions")) {
     99                    states.push(currentState);
     100                    currentState = state.ext;
     101                }
     102                break;
     103            case author:
     104                if (qName.equals("link")) {
     105                    states.push(currentState);
     106                    currentState = state.link;
     107                    currentLink = new GpxLink(atts.getValue("href"));
     108                }
     109                break;
     110            case trk:
     111                if (qName.equals("trkseg")) {
     112                    states.push(currentState);
     113                    currentState = state.trkseg;
     114                    currentTrackSeg = new ArrayList<WayPoint>();
     115                } else if (qName.equals("link")) {
     116                    states.push(currentState);
     117                    currentState = state.link;
     118                    currentLink = new GpxLink(atts.getValue("href"));
     119                } else if (qName.equals("extensions")) {
     120                    states.push(currentState);
     121                    currentState = state.ext;
     122                }
     123                break;
     124            case metadata:
     125                if (qName.equals("author")) {
     126                    states.push(currentState);
     127                    currentState = state.author;
     128                } else if (qName.equals("extensions")) {
     129                    states.push(currentState);
     130                    currentState = state.ext;
     131                }
     132                break;
     133            case trkseg:
     134                if (qName.equals("trkpt")) {
     135                    states.push(currentState);
     136                    currentState = state.wpt;
     137                    currentWayPoint = new WayPoint(parseLatLon(atts));
     138                }
     139                break;
     140            case wpt:
     141                if (qName.equals("link")) {
     142                    states.push(currentState);
     143                    currentState = state.link;
     144                    currentLink = new GpxLink(atts.getValue("href"));
     145                } else if (qName.equals("extensions")) {
     146                    states.push(currentState);
     147                    currentState = state.ext;
     148                }
     149                break;
     150            case rte:
     151                if (qName.equals("link")) {
     152                    states.push(currentState);
     153                    currentState = state.link;
     154                    currentLink = new GpxLink(atts.getValue("href"));
     155                } else if (qName.equals("rtept")) {
     156                    states.push(currentState);
     157                    currentState = state.wpt;
     158                    currentWayPoint = new WayPoint(parseLatLon(atts));
     159                } else if (qName.equals("extensions")) {
     160                    states.push(currentState);
     161                    currentState = state.ext;
     162                }
     163                break;
     164            default:
     165            }
     166            accumulator.setLength(0);
     167        }
     168
     169        @Override public void characters(char[] ch, int start, int length) {
     170            accumulator.append(ch, start, length);
     171        }
     172
     173        private Map<String, Object> getAttr() {
     174            switch (currentState) {
     175                case rte: return currentRoute.attr;
     176                case metadata: return currentData.attr;
     177                case wpt: return currentWayPoint.attr;
     178                case trk: return currentTrack.attr;
     179                default: return null;
     180            }
     181        }
     182
     183        @Override public void endElement(String namespaceURI, String qName, String rqName) {
     184            switch (currentState) {
     185            case metadata:
     186                if (qName.equals("name") || qName.equals("desc") ||
     187                        qName.equals("time") || qName.equals("keywords")) {
     188                    currentData.attr.put(qName, accumulator.toString());
     189                } else if (qName.equals("metadata")) {
     190                    currentState = states.pop();
     191                }
     192                //TODO: parse copyright, bounds, extensions
     193                break;
     194            case author:
     195                if (qName.equals("author")) {
     196                    currentState = states.pop();
     197                } else if (qName.equals("name") || qName.equals("email")) {
     198                    currentData.attr.put("author" + qName, accumulator.toString());
     199                } else if (qName.equals("link")) {
     200                    currentData.attr.put("authorlink", currentLink);
     201                }
     202                break;
     203            case link:
     204                if (qName.equals("text")) {
     205                    currentLink.text = accumulator.toString();
     206                } else if (qName.equals("type")) {
     207                    currentLink.type = accumulator.toString();
     208                } else if (qName.equals("link")) {
     209                    // <link>URL</link>
     210                    if (currentLink.uri == null)
     211                        currentLink.uri = accumulator.toString();
     212
     213                    currentState = states.pop();
     214                }
     215                if (currentState == state.author) {
     216                    currentData.attr.put("authorlink", currentLink);
     217                } else if (currentState != state.link) {
     218                    Map<String, Object> attr = getAttr();
     219                    if (!attr.containsKey("link")) {
     220                        attr.put("link", new LinkedList<GpxLink>());
     221                    }
     222                    ((Collection<GpxLink>) attr.get("link")).add(currentLink);
     223                }
     224                break;
     225            case wpt:
     226                if (qName.equals("ele") || qName.equals("magvar")
     227                        || qName.equals("geoidheight") || qName.equals("name")
     228                        || qName.equals("sym") || qName.equals("type")) {
     229                    currentWayPoint.attr.put(qName, accumulator.toString());
     230                } else if (qName.equals("time")) {
     231                    currentWayPoint.attr.put(qName, accumulator.toString());
     232                    currentWayPoint.setTime();
     233                } else if (qName.equals("cmt") || qName.equals("desc")) {
     234                    currentWayPoint.attr.put(qName, accumulator.toString());
     235                    currentWayPoint.setGarminCommentTime(qName);
     236                } else if (qName.equals("rtept")) {
     237                    currentState = states.pop();
     238                    currentRoute.routePoints.add(currentWayPoint);
     239                } else if (qName.equals("trkpt")) {
     240                    currentState = states.pop();
     241                    currentTrackSeg.add(currentWayPoint);
     242                } else if (qName.equals("wpt")) {
     243                    currentState = states.pop();
     244                    currentData.waypoints.add(currentWayPoint);
     245                }
     246                break;
     247            case trkseg:
     248                if (qName.equals("trkseg")) {
     249                    currentState = states.pop();
     250                    currentTrack.trackSegs.add(currentTrackSeg);
     251                }
     252                break;
     253            case trk:
     254                if (qName.equals("trk")) {
     255                    currentState = states.pop();
     256                    currentData.tracks.add(currentTrack);
     257                } else if (qName.equals("name") || qName.equals("cmt")
     258                        || qName.equals("desc") || qName.equals("src")
     259                        || qName.equals("type") || qName.equals("number")) {
     260                    currentTrack.attr.put(qName, accumulator.toString());
     261                }
     262                break;
     263            case ext:
     264                if (qName.equals("extensions")) {
     265                    currentState = states.pop();
     266                }
     267                break;
     268            default:
     269                if (qName.equals("wpt")) {
     270                    currentState = states.pop();
     271                } else if (qName.equals("rte")) {
     272                    currentState = states.pop();
     273                    currentData.routes.add(currentRoute);
     274                }
     275            }
     276        }
     277
     278        @Override public void endDocument() throws SAXException  {
     279            if (!states.empty()) {
     280                throw new SAXException(tr("Parse error: invalid document structure for gpx document"));
     281            }
     282            data = currentData;
     283        }
     284    }
     285
     286    /**
     287    * Parse the input stream and store the result in trackData and markerData
     288     *
     289     * @param relativeMarkerPath The directory to use as relative path for all &lt;wpt&gt;
     290     *    marker tags. Maybe <code>null</code>, in which case no relative urls are constructed for the markers.
     291    */
     292    public GpxReader(InputStream source, File relativeMarkerPath) throws SAXException, IOException {
     293
     294        Parser parser = new Parser();
     295        InputSource inputSource = new InputSource(new InputStreamReader(source, "UTF-8"));
     296        try {
     297            SAXParserFactory factory = SAXParserFactory.newInstance();
     298            factory.setNamespaceAware(true);
     299            factory.newSAXParser().parse(inputSource, parser);
     300        } catch (ParserConfigurationException e) {
     301            e.printStackTrace(); // broken SAXException chaining
     302            throw new SAXException(e);
     303        }
     304    }
    305305}
  • trunk/src/org/openstreetmap/josm/io/GpxWriter.java

    r1164 r1169  
    9797        for (WayPoint pnt : data.waypoints) {
    9898            wayPoint(pnt, WAY_POINT);
    99         }       
    100     }
    101    
     99        }
     100    }
     101
    102102    private void writeRoutes() {
    103103        for (GpxRoute rte : data.routes) {
     
    110110        }
    111111    }
    112    
     112
    113113    private void writeTracks() {
    114114        for (GpxTrack trk : data.tracks) {
     
    140140        indent += "  ";
    141141    }
    142    
     142
    143143    private void inline(String tag, String attributes) {
    144144        out.println(indent + "<" + tag + " " + attributes + " />");
     
    155155    }
    156156
    157     /**       
     157    /**
    158158     * if content not null, open tag, write encoded content, and close tag
    159159     * else do nothing.
     
    168168    }
    169169
    170     /**       
     170    /**
    171171     * output link
    172172     */
     
    180180    }
    181181
    182     /**       
     182    /**
    183183     * output a point
    184184     */
  • trunk/src/org/openstreetmap/josm/io/MirroredInputStream.java

    r999 r1169  
    2020 */
    2121public class MirroredInputStream extends InputStream {
    22         InputStream fs = null;
     22    InputStream fs = null;
    2323
    24         public MirroredInputStream(String name) throws IOException
    25         {
    26                 this(name, null, -1L);
    27         }
     24    public MirroredInputStream(String name) throws IOException
     25    {
     26        this(name, null, -1L);
     27    }
    2828
    29         public MirroredInputStream(String name, long maxTime) throws IOException
    30         {
    31                 this(name, null, maxTime);
    32         }
     29    public MirroredInputStream(String name, long maxTime) throws IOException
     30    {
     31        this(name, null, maxTime);
     32    }
    3333
    34         public MirroredInputStream(String name, String destDir, long maxTime) throws IOException
    35         {
    36                 URL url;
    37                 File file = null;
    38                 try
    39                 {
    40                         url = new URL(name);
    41                         if(url.getProtocol().equals("file"))
    42                         {
    43                                 file = new File(name.substring("file:/".length()));
    44                                 if(!file.exists())
    45                                         file = new File(name.substring("file://".length()));
    46                         }
    47                         else
    48                                 file = checkLocal(url, destDir, maxTime);
    49                 }
    50                 catch(java.net.MalformedURLException e)
    51                 {
    52                         if(name.startsWith("resource://"))
    53                         {
    54                                 fs = getClass().getResourceAsStream(
    55                                 name.substring("resource:/".length()));
    56                                 return;
    57                         }
    58                         else
    59                                 file = new File(name);
    60                 }
    61                 if(file == null)
    62                         throw new IOException();
    63                 fs = new FileInputStream(file);
    64         }
     34    public MirroredInputStream(String name, String destDir, long maxTime) throws IOException
     35    {
     36        URL url;
     37        File file = null;
     38        try
     39        {
     40            url = new URL(name);
     41            if(url.getProtocol().equals("file"))
     42            {
     43                file = new File(name.substring("file:/".length()));
     44                if(!file.exists())
     45                    file = new File(name.substring("file://".length()));
     46            }
     47            else
     48                file = checkLocal(url, destDir, maxTime);
     49        }
     50        catch(java.net.MalformedURLException e)
     51        {
     52            if(name.startsWith("resource://"))
     53            {
     54                fs = getClass().getResourceAsStream(
     55                name.substring("resource:/".length()));
     56                return;
     57            }
     58            else
     59                file = new File(name);
     60        }
     61        if(file == null)
     62            throw new IOException();
     63        fs = new FileInputStream(file);
     64    }
    6565
    66         private File checkLocal(URL url, String destDir, long maxTime)
    67         {
    68                 String localPath = Main.pref.get("mirror." + url);
    69                 File file = null;
    70                 if(localPath != null && localPath.length() > 0)
    71                 {
    72                         String[] lp = localPath.split(";");
    73                         file = new File(lp[1]);
    74                         if(maxTime <= 0)
    75                                 maxTime = Main.pref.getInteger("mirror.maxtime", 7*24*60*60);
    76                         if(System.currentTimeMillis() - Long.parseLong(lp[0]) < maxTime*1000)
    77                         {
    78                                 if(file.exists())
    79                                 {
    80                                         return file;
    81                                 }
    82                         }
    83                 }
    84                 if(destDir == null)
    85                         destDir = Main.pref.getPreferencesDir();
     66    private File checkLocal(URL url, String destDir, long maxTime)
     67    {
     68        String localPath = Main.pref.get("mirror." + url);
     69        File file = null;
     70        if(localPath != null && localPath.length() > 0)
     71        {
     72            String[] lp = localPath.split(";");
     73            file = new File(lp[1]);
     74            if(maxTime <= 0)
     75                maxTime = Main.pref.getInteger("mirror.maxtime", 7*24*60*60);
     76            if(System.currentTimeMillis() - Long.parseLong(lp[0]) < maxTime*1000)
     77            {
     78                if(file.exists())
     79                {
     80                    return file;
     81                }
     82            }
     83        }
     84        if(destDir == null)
     85            destDir = Main.pref.getPreferencesDir();
    8686
    87                 File destDirFile = new File(destDir);
    88                 if(!destDirFile.exists() )
    89                         destDirFile.mkdirs();
     87        File destDirFile = new File(destDir);
     88        if(!destDirFile.exists() )
     89            destDirFile.mkdirs();
    9090
    91                 localPath = "mirror_" + new File(url.getPath()).getName();
    92                 destDirFile = new File(destDir, localPath + ".tmp");
    93                 BufferedOutputStream bos = null;
    94                 BufferedInputStream bis = null;
    95                 try
    96                 {
    97                         URLConnection conn = url.openConnection();
    98                         conn.setConnectTimeout(5000);
    99                         bis = new BufferedInputStream(conn.getInputStream());
    100                         bos = new BufferedOutputStream( new FileOutputStream(destDirFile));
    101                         byte[] buffer = new byte[4096];
    102                         int length;
    103                         while((length = bis.read(buffer)) > -1)
    104                                 bos.write(buffer, 0, length);
    105                 }
    106                 catch(IOException ioe)
    107                 {
    108                         if(file != null)
    109                                 return file;
    110                         else
    111                                 return null;
    112                 }
    113                 finally
    114                 {
    115                         if(bis != null)
    116                         {
    117                                 try
    118                                 {
    119                                         bis.close();
    120                                 }
    121                                 catch (IOException e)
    122                                 {
    123                                         e.printStackTrace();
    124                                 }
    125                         }
    126                         if(bos != null)
    127                         {
    128                                 try
    129                                 {
    130                                         bos.close();
    131                                 }
    132                                 catch (IOException e)
    133                                 {
    134                                         e.printStackTrace();
    135                                 }
    136                         }
    137                         file = new File(destDir, localPath);
    138                         destDirFile.renameTo(file);
    139                         Main.pref.put("mirror." + url, System.currentTimeMillis() + ";" + file);
    140                 }
     91        localPath = "mirror_" + new File(url.getPath()).getName();
     92        destDirFile = new File(destDir, localPath + ".tmp");
     93        BufferedOutputStream bos = null;
     94        BufferedInputStream bis = null;
     95        try
     96        {
     97            URLConnection conn = url.openConnection();
     98            conn.setConnectTimeout(5000);
     99            bis = new BufferedInputStream(conn.getInputStream());
     100            bos = new BufferedOutputStream( new FileOutputStream(destDirFile));
     101            byte[] buffer = new byte[4096];
     102            int length;
     103            while((length = bis.read(buffer)) > -1)
     104                bos.write(buffer, 0, length);
     105        }
     106        catch(IOException ioe)
     107        {
     108            if(file != null)
     109                return file;
     110            else
     111                return null;
     112        }
     113        finally
     114        {
     115            if(bis != null)
     116            {
     117                try
     118                {
     119                    bis.close();
     120                }
     121                catch (IOException e)
     122                {
     123                    e.printStackTrace();
     124                }
     125            }
     126            if(bos != null)
     127            {
     128                try
     129                {
     130                    bos.close();
     131                }
     132                catch (IOException e)
     133                {
     134                    e.printStackTrace();
     135                }
     136            }
     137            file = new File(destDir, localPath);
     138            destDirFile.renameTo(file);
     139            Main.pref.put("mirror." + url, System.currentTimeMillis() + ";" + file);
     140        }
    141141
    142                 return file;
    143         }
    144         public int available() throws IOException
    145         { return fs.available(); }
    146         public void close() throws IOException
    147         { fs.close(); }
    148         public int read() throws IOException
    149         { return fs.read(); }
    150         public int read(byte[] b) throws IOException
    151         { return fs.read(b); }
    152         public int read(byte[] b, int off, int len) throws IOException
    153         { return fs.read(b,off, len); }
    154         public long skip(long n) throws IOException
    155         { return fs.skip(n); }
     142        return file;
     143    }
     144    public int available() throws IOException
     145    { return fs.available(); }
     146    public void close() throws IOException
     147    { fs.close(); }
     148    public int read() throws IOException
     149    { return fs.read(); }
     150    public int read(byte[] b) throws IOException
     151    { return fs.read(b); }
     152    public int read(byte[] b, int off, int len) throws IOException
     153    { return fs.read(b,off, len); }
     154    public long skip(long n) throws IOException
     155    { return fs.skip(n); }
    156156}
  • trunk/src/org/openstreetmap/josm/io/MultiPartFormOutputStream.java

    r627 r1169  
    33
    44License
    5                                
     5
    66Copyright 1994-2007 Sun Microsystems, Inc. All Rights Reserved.
    7 Redistribution and use in source and binary forms, with or without modification, 
     7Redistribution and use in source and binary forms, with or without modification,
    88are permitted provided that the following conditions are met:
    9  
    10     * Redistribution of source code must retain the above copyright notice, this list 
     9
     10    * Redistribution of source code must retain the above copyright notice, this list
    1111      of conditions and the following disclaimer.
    1212
    13     * Redistribution in binary form must reproduce the above copyright notice, this 
    14       list of conditions and the following disclaimer in the documentation and/or other 
     13    * Redistribution in binary form must reproduce the above copyright notice, this
     14      list of conditions and the following disclaimer in the documentation and/or other
    1515      materials provided with the distribution.
    1616
    17  
    18 Neither the name of Sun Microsystems, Inc. or the names of contributors may be used to 
    19 endorse or promote products derived from this software without specific prior written 
     17
     18Neither the name of Sun Microsystems, Inc. or the names of contributors may be used to
     19endorse or promote products derived from this software without specific prior written
    2020permission.
    21  
    22 This software is provided "AS IS," without a warranty of any kind. ALL EXPRESS OR IMPLIED 
    23 CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, 
    24 FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MICROSYSTEMS, 
    25 INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS 
    26 A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT 
    27 WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, 
    28 INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND 
    29 REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS 
     21
     22This software is provided "AS IS," without a warranty of any kind. ALL EXPRESS OR IMPLIED
     23CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
     24FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MICROSYSTEMS,
     25INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS
     26A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT
     27WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT,
     28INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
     29REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS
    3030SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
    31  
    32 You acknowledge that this software is not designed, licensed or intended for use in the 
    33 design, construction, operation or maintenance of any nuclear facility. 
     31
     32You acknowledge that this software is not designed, licensed or intended for use in the
     33design, construction, operation or maintenance of any nuclear facility.
    3434*/
    3535
     
    4444import java.net.URLConnection;
    4545
    46          
     46
    4747/**
    48  * <code>MultiPartFormOutputStream</code> is used to write 
    49  * "multipart/form-data" to a <code>java.net.URLConnection</code> for 
    50  * POSTing.  This is primarily for file uploading to HTTP servers. 
    51  * 
     48 * <code>MultiPartFormOutputStream</code> is used to write
     49 * "multipart/form-data" to a <code>java.net.URLConnection</code> for
     50 * POSTing.  This is primarily for file uploading to HTTP servers.
     51 *
    5252 * @since  JDK1.3
    5353 */
    5454public class MultiPartFormOutputStream extends OsmConnection {
    55         /**
    56          * The line end characters. 
    57         */
    58         private static final String NEWLINE = "\r\n";
    59 
    60         /**
    61          * The boundary prefix. 
    62         */
    63         private static final String PREFIX = "--";
    64 
    65         /**
    66          * The output stream to write to. 
    67         */
    68         private DataOutputStream out = null;
    69 
    70         /**
    71          * The multipart boundary string. 
    72         */
    73         private String boundary = null;
    74 
    75         /**
    76          * Creates a new <code>MultiPartFormOutputStream</code> object using
    77          * the specified output stream and boundary.  The boundary is required
    78          * to be created before using this method, as described in the
    79          * description for the <code>getContentType(String)</code> method. 
    80          * The boundary is only checked for <code>null</code> or empty string,
    81          * but it is recommended to be at least 6 characters.  (Or use the
    82         * static createBoundary() method to create one.)
    83          *
    84         * @param  os        the output stream
    85         * @param  boundary  the boundary
    86         * @see  #createBoundary()
    87         * @see  #getContentType(String)
    88         */
    89         public MultiPartFormOutputStream(OutputStream os, String boundary) {
    90                 if(os == null) {
    91                         throw new IllegalArgumentException("Output stream is required.");
    92                 }
    93                 if(boundary == null || boundary.length() == 0) {
    94                         throw new IllegalArgumentException("Boundary stream is required.");
    95                 }
    96                 this.out = new DataOutputStream(os);
    97                 this.boundary = boundary;
    98                 initAuthentication();
    99         }
    100 
    101         /**
    102          * Writes an boolean field value. 
    103          *
    104         * @param  name   the field name (required)
    105         * @param  value  the field value
    106         * @throws  java.io.IOException  on input/output errors
    107         */
    108         public void writeField(String name, boolean value)
    109         throws java.io.IOException {
    110                 writeField(name, new Boolean(value).toString());
    111         }
    112 
    113         /**
    114          * Writes an double field value. 
    115          *
    116         * @param  name   the field name (required)
    117         * @param  value  the field value
    118         * @throws  java.io.IOException  on input/output errors
    119         */
    120         public void writeField(String name, double value)
    121         throws java.io.IOException {
    122                 writeField(name, Double.toString(value));
    123         }
    124 
    125         /**
    126          * Writes an float field value. 
    127          *
    128         * @param  name   the field name (required)
    129         * @param  value  the field value
    130         * @throws  java.io.IOException  on input/output errors
    131         */
    132         public void writeField(String name, float value)
    133         throws java.io.IOException {
    134                 writeField(name, Float.toString(value));
    135         }
    136 
    137         /**
    138          * Writes an long field value. 
    139          *
    140         * @param  name   the field name (required)
    141         * @param  value  the field value
    142         * @throws  java.io.IOException  on input/output errors
    143         */
    144         public void writeField(String name, long value)
    145         throws java.io.IOException {
    146                 writeField(name, Long.toString(value));
    147         }
    148 
    149         /**
    150          * Writes an int field value. 
    151          *
    152         * @param  name   the field name (required)
    153         * @param  value  the field value
    154         * @throws  java.io.IOException  on input/output errors
    155         */
    156         public void writeField(String name, int value)
    157         throws java.io.IOException {
    158                 writeField(name, Integer.toString(value));
    159         }
    160 
    161         /**
    162          * Writes an short field value. 
    163          *
    164         * @param  name   the field name (required)
    165         * @param  value  the field value
    166         * @throws  java.io.IOException  on input/output errors
    167         */
    168         public void writeField(String name, short value)
    169         throws java.io.IOException {
    170                 writeField(name, Short.toString(value));
    171         }
    172 
    173         /**
    174          * Writes an char field value. 
    175          *
    176         * @param  name   the field name (required)
    177         * @param  value  the field value
    178         * @throws  java.io.IOException  on input/output errors
    179         */
    180         public void writeField(String name, char value)
    181         throws java.io.IOException {
    182                 writeField(name, new Character(value).toString());
    183         }
    184 
    185         /**
    186          * Writes an string field value.  If the value is null, an empty string
    187          * is sent (""). 
    188          *
    189         * @param  name   the field name (required)
    190         * @param  value  the field value
    191         * @throws  java.io.IOException  on input/output errors
    192         */
    193         public void writeField(String name, String value)
    194         throws java.io.IOException {
    195                 if(name == null) {
    196                         throw new IllegalArgumentException("Name cannot be null or empty.");
    197                 }
    198                 if(value == null) {
    199                         value = "";
    200                 }
    201                 /*
    202                         --boundary\r\n
    203                         Content-Disposition: form-data; name="<fieldName>"\r\n
    204                         \r\n
    205                         <value>\r\n
    206                 */
    207                 // write boundary
    208                 out.writeBytes(PREFIX);
    209                 out.writeBytes(boundary);
    210                 out.writeBytes(NEWLINE);
    211                 // write content header
    212                 out.writeBytes("Content-Disposition: form-data; name=\"" + name + "\"");
    213                 out.writeBytes(NEWLINE);
    214                 out.writeBytes(NEWLINE);
    215                 // write content
    216                 out.writeBytes(value);
    217                 out.writeBytes(NEWLINE);
    218                 out.flush();
    219         }
    220 
    221         /**
    222          * Writes a file's contents.  If the file is null, does not exists, or
    223          * is a directory, a <code>java.lang.IllegalArgumentException</code>
    224          * will be thrown. 
    225          *
    226         * @param  name      the field name
    227         * @param  mimeType  the file content type (optional, recommended)
    228         * @param  file      the file (the file must exist)
    229         * @throws  java.io.IOException  on input/output errors
    230         */
    231         public void writeFile(String name, String mimeType, java.io.File file)
    232         throws java.io.IOException {
    233                 if(file == null) {
    234                         throw new IllegalArgumentException("File cannot be null.");
    235                 }
    236                 if(!file.exists()) {
    237                         throw new IllegalArgumentException("File does not exist.");
    238                 }
    239                 if(file.isDirectory()) {
    240                         throw new IllegalArgumentException("File cannot be a directory.");
    241                 }
    242                 writeFile(name, mimeType, file.getCanonicalPath(), new FileInputStream(file));
    243         }
    244 
    245         /**
    246          * Writes a input stream's contents.  If the input stream is null, a
    247          * <code>java.lang.IllegalArgumentException</code> will be thrown. 
    248          *
    249         * @param  name      the field name
    250         * @param  mimeType  the file content type (optional, recommended)
    251         * @param  fileName  the file name (required)
    252         * @param  is        the input stream
    253         * @throws  java.io.IOException  on input/output errors
    254         */
    255         public void writeFile(String name, String mimeType,
    256                         String fileName, InputStream is)
    257         throws java.io.IOException {
    258                 if(is == null) {
    259                         throw new IllegalArgumentException("Input stream cannot be null.");
    260                 }
    261                 if(fileName == null || fileName.length() == 0) {
    262                         throw new IllegalArgumentException("File name cannot be null or empty.");
    263                 }
    264                 /*
    265                         --boundary\r\n
    266                         Content-Disposition: form-data; name="<fieldName>"; filename="<filename>"\r\n
    267                         Content-Type: <mime-type>\r\n
    268                         \r\n
    269                         <file-data>\r\n
    270                 */
    271                 // write boundary
    272                 out.writeBytes(PREFIX);
    273                 out.writeBytes(boundary);
    274                 out.writeBytes(NEWLINE);
    275                 // write content header
    276                 out.writeBytes("Content-Disposition: form-data; name=\"" + name +
    277                                 "\"; filename=\"" + fileName + "\"");
    278                 out.writeBytes(NEWLINE);
    279                 if(mimeType != null) {
    280                         out.writeBytes("Content-Type: " + mimeType);
    281                         out.writeBytes(NEWLINE);
    282                 }
    283                 out.writeBytes(NEWLINE);
    284                 // write content
    285                 byte[] data = new byte[1024];
    286                 int r = 0;
    287                 while((r = is.read(data, 0, data.length)) != -1) {
    288                         out.write(data, 0, r);
    289                 }
    290                 // close input stream, but ignore any possible exception for it
    291                 try {
    292                         is.close();
    293                 } catch(Exception e) {}
    294                 out.writeBytes(NEWLINE);
    295                 out.flush();
    296         }
    297 
    298         /**
    299          * Writes the given bytes.  The bytes are assumed to be the contents
    300          * of a file, and will be sent as such.  If the data is null, a
    301          * <code>java.lang.IllegalArgumentException</code> will be thrown. 
    302          *
    303         * @param  name      the field name
    304         * @param  mimeType  the file content type (optional, recommended)
    305         * @param  fileName  the file name (required)
    306         * @param  data      the file data
    307         * @throws  java.io.IOException  on input/output errors
    308         */
    309         public void writeFile(String name, String mimeType,
    310                         String fileName, byte[] data)
    311         throws java.io.IOException {
    312                 if(data == null) {
    313                         throw new IllegalArgumentException("Data cannot be null.");
    314                 }
    315                 if(fileName == null || fileName.length() == 0) {
    316                         throw new IllegalArgumentException("File name cannot be null or empty.");
    317                 }
    318                 /*
    319                         --boundary\r\n
    320                         Content-Disposition: form-data; name="<fieldName>"; filename="<filename>"\r\n
    321                         Content-Type: <mime-type>\r\n
    322                         \r\n
    323                         <file-data>\r\n
    324                 */
    325                 // write boundary
    326                 out.writeBytes(PREFIX);
    327                 out.writeBytes(boundary);
    328                 out.writeBytes(NEWLINE);
    329                 // write content header
    330                 out.writeBytes("Content-Disposition: form-data; name=\"" + name +
    331                                 "\"; filename=\"" + fileName + "\"");
    332                 out.writeBytes(NEWLINE);
    333                 if(mimeType != null) {
    334                         out.writeBytes("Content-Type: " + mimeType);
    335                         out.writeBytes(NEWLINE);
    336                 }
    337                 out.writeBytes(NEWLINE);
    338                 // write content
    339                 out.write(data, 0, data.length);
    340                 out.writeBytes(NEWLINE);
    341                 out.flush();
    342         }
    343 
    344         /**
    345          * Flushes the stream.  Actually, this method does nothing, as the only
    346          * write methods are highly specialized and automatically flush. 
    347         */
    348         public void flush() {
    349                 // out.flush();
    350         }
    351 
    352         /**
    353         * Closes the stream.  <br/>
    354         * <br/>
    355          * <b>NOTE:</b> This method <b>MUST</b> be called to finalize the
    356         * multipart stream.
    357          *
    358         * @throws  java.io.IOException  on input/output errors
    359         */
    360         public void close() throws java.io.IOException {
    361                 // write final boundary
    362                 out.writeBytes(PREFIX);
    363                 out.writeBytes(boundary);
    364                 out.writeBytes(PREFIX);
    365                 out.writeBytes(NEWLINE);
    366                 out.flush();
    367                 out.close();
    368         }
    369 
    370         /**
    371          * Gets the multipart boundary string being used by this stream. 
    372          *
    373         * @return  the boundary
    374         */
    375         public String getBoundary() {
    376                 return this.boundary;
    377         }
    378 
    379         /**
    380          * Creates a new <code>java.net.URLConnection</code> object from the
    381          * specified <code>java.net.URL</code>.  This is a convenience method
    382          * which will set the <code>doInput</code>, <code>doOutput</code>,
    383          * <code>useCaches</code> and <code>defaultUseCaches</code> fields to
    384          * the appropriate settings in the correct order. 
    385          *
    386         * @return  a <code>java.net.URLConnection</code> object for the URL
    387         * @throws  java.io.IOException  on input/output errors
    388         */
    389         public static URLConnection createConnection(URL url)
    390         throws java.io.IOException {
    391                 URLConnection urlConn = url.openConnection();
    392                 if(urlConn instanceof HttpURLConnection) {
    393                         HttpURLConnection httpConn = (HttpURLConnection)urlConn;
    394                         httpConn.setRequestMethod("POST");
    395                 }
    396                 urlConn.setDoInput(true);
    397                 urlConn.setDoOutput(true);
    398                 urlConn.setUseCaches(false);
    399                 urlConn.setDefaultUseCaches(false);
    400                 return urlConn;
    401         }
    402 
    403         /**
    404          * Creates a multipart boundary string by concatenating 20 hyphens (-)
    405          * and the hexadecimal (base-16) representation of the current time in
    406          * milliseconds. 
    407          *
    408         * @return  a multipart boundary string
    409         * @see  #getContentType(String)
    410         */
    411         public static String createBoundary() {
    412                 return "--------------------" +
    413                 Long.toString(System.currentTimeMillis(), 16);
    414         }
    415 
    416         /**
    417          * Gets the content type string suitable for the
    418          * <code>java.net.URLConnection</code> which includes the multipart
    419         * boundary string.  <br/>
    420         * <br/>
    421          * This method is static because, due to the nature of the
    422          * <code>java.net.URLConnection</code> class, once the output stream
    423          * for the connection is acquired, it's too late to set the content
    424          * type (or any other request parameter).  So one has to create a
    425          * multipart boundary string first before using this class, such as
    426          * with the <code>createBoundary()</code> method. 
    427          *
    428         * @param  boundary  the boundary string
    429         * @return  the content type string
    430         * @see  #createBoundary()
    431         */
    432         public static String getContentType(String boundary) {
    433                 return "multipart/form-data; boundary=" + boundary;
    434         }
     55    /**
     56     * The line end characters.
     57    */
     58    private static final String NEWLINE = "\r\n";
     59
     60    /**
     61     * The boundary prefix.
     62    */
     63    private static final String PREFIX = "--";
     64
     65    /**
     66     * The output stream to write to.
     67    */
     68    private DataOutputStream out = null;
     69
     70    /**
     71     * The multipart boundary string.
     72    */
     73    private String boundary = null;
     74
     75    /**
     76     * Creates a new <code>MultiPartFormOutputStream</code> object using
     77     * the specified output stream and boundary.  The boundary is required
     78     * to be created before using this method, as described in the
     79     * description for the <code>getContentType(String)</code> method.
     80     * The boundary is only checked for <code>null</code> or empty string,
     81     * but it is recommended to be at least 6 characters.  (Or use the
     82    * static createBoundary() method to create one.)
     83     *
     84    * @param  os        the output stream
     85    * @param  boundary  the boundary
     86    * @see  #createBoundary()
     87    * @see  #getContentType(String)
     88    */
     89    public MultiPartFormOutputStream(OutputStream os, String boundary) {
     90        if(os == null) {
     91            throw new IllegalArgumentException("Output stream is required.");
     92        }
     93        if(boundary == null || boundary.length() == 0) {
     94            throw new IllegalArgumentException("Boundary stream is required.");
     95        }
     96        this.out = new DataOutputStream(os);
     97        this.boundary = boundary;
     98        initAuthentication();
     99    }
     100
     101    /**
     102     * Writes an boolean field value.
     103     *
     104    * @param  name   the field name (required)
     105    * @param  value  the field value
     106    * @throws  java.io.IOException  on input/output errors
     107    */
     108    public void writeField(String name, boolean value)
     109    throws java.io.IOException {
     110        writeField(name, new Boolean(value).toString());
     111    }
     112
     113    /**
     114     * Writes an double field value.
     115     *
     116    * @param  name   the field name (required)
     117    * @param  value  the field value
     118    * @throws  java.io.IOException  on input/output errors
     119    */
     120    public void writeField(String name, double value)
     121    throws java.io.IOException {
     122        writeField(name, Double.toString(value));
     123    }
     124
     125    /**
     126     * Writes an float field value.
     127     *
     128    * @param  name   the field name (required)
     129    * @param  value  the field value
     130    * @throws  java.io.IOException  on input/output errors
     131    */
     132    public void writeField(String name, float value)
     133    throws java.io.IOException {
     134        writeField(name, Float.toString(value));
     135    }
     136
     137    /**
     138     * Writes an long field value.
     139     *
     140    * @param  name   the field name (required)
     141    * @param  value  the field value
     142    * @throws  java.io.IOException  on input/output errors
     143    */
     144    public void writeField(String name, long value)
     145    throws java.io.IOException {
     146        writeField(name, Long.toString(value));
     147    }
     148
     149    /**
     150     * Writes an int field value.
     151     *
     152    * @param  name   the field name (required)
     153    * @param  value  the field value
     154    * @throws  java.io.IOException  on input/output errors
     155    */
     156    public void writeField(String name, int value)
     157    throws java.io.IOException {
     158        writeField(name, Integer.toString(value));
     159    }
     160
     161    /**
     162     * Writes an short field value.
     163     *
     164    * @param  name   the field name (required)
     165    * @param  value  the field value
     166    * @throws  java.io.IOException  on input/output errors
     167    */
     168    public void writeField(String name, short value)
     169    throws java.io.IOException {
     170        writeField(name, Short.toString(value));
     171    }
     172
     173    /**
     174     * Writes an char field value.
     175     *
     176    * @param  name   the field name (required)
     177    * @param  value  the field value
     178    * @throws  java.io.IOException  on input/output errors
     179    */
     180    public void writeField(String name, char value)
     181    throws java.io.IOException {
     182        writeField(name, new Character(value).toString());
     183    }
     184
     185    /**
     186     * Writes an string field value.  If the value is null, an empty string
     187     * is sent ("").
     188     *
     189    * @param  name   the field name (required)
     190    * @param  value  the field value
     191    * @throws  java.io.IOException  on input/output errors
     192    */
     193    public void writeField(String name, String value)
     194    throws java.io.IOException {
     195        if(name == null) {
     196            throw new IllegalArgumentException("Name cannot be null or empty.");
     197        }
     198        if(value == null) {
     199            value = "";
     200        }
     201        /*
     202            --boundary\r\n
     203            Content-Disposition: form-data; name="<fieldName>"\r\n
     204            \r\n
     205            <value>\r\n
     206        */
     207        // write boundary
     208        out.writeBytes(PREFIX);
     209        out.writeBytes(boundary);
     210        out.writeBytes(NEWLINE);
     211        // write content header
     212        out.writeBytes("Content-Disposition: form-data; name=\"" + name + "\"");
     213        out.writeBytes(NEWLINE);
     214        out.writeBytes(NEWLINE);
     215        // write content
     216        out.writeBytes(value);
     217        out.writeBytes(NEWLINE);
     218        out.flush();
     219    }
     220
     221    /**
     222     * Writes a file's contents.  If the file is null, does not exists, or
     223     * is a directory, a <code>java.lang.IllegalArgumentException</code>
     224     * will be thrown.
     225     *
     226    * @param  name      the field name
     227    * @param  mimeType  the file content type (optional, recommended)
     228    * @param  file      the file (the file must exist)
     229    * @throws  java.io.IOException  on input/output errors
     230    */
     231    public void writeFile(String name, String mimeType, java.io.File file)
     232    throws java.io.IOException {
     233        if(file == null) {
     234            throw new IllegalArgumentException("File cannot be null.");
     235        }
     236        if(!file.exists()) {
     237            throw new IllegalArgumentException("File does not exist.");
     238        }
     239        if(file.isDirectory()) {
     240            throw new IllegalArgumentException("File cannot be a directory.");
     241        }
     242        writeFile(name, mimeType, file.getCanonicalPath(), new FileInputStream(file));
     243    }
     244
     245    /**
     246     * Writes a input stream's contents.  If the input stream is null, a
     247     * <code>java.lang.IllegalArgumentException</code> will be thrown.
     248     *
     249    * @param  name      the field name
     250    * @param  mimeType  the file content type (optional, recommended)
     251    * @param  fileName  the file name (required)
     252    * @param  is        the input stream
     253    * @throws  java.io.IOException  on input/output errors
     254    */
     255    public void writeFile(String name, String mimeType,
     256            String fileName, InputStream is)
     257    throws java.io.IOException {
     258        if(is == null) {
     259            throw new IllegalArgumentException("Input stream cannot be null.");
     260        }
     261        if(fileName == null || fileName.length() == 0) {
     262            throw new IllegalArgumentException("File name cannot be null or empty.");
     263        }
     264        /*
     265            --boundary\r\n
     266            Content-Disposition: form-data; name="<fieldName>"; filename="<filename>"\r\n
     267            Content-Type: <mime-type>\r\n
     268            \r\n
     269            <file-data>\r\n
     270        */
     271        // write boundary
     272        out.writeBytes(PREFIX);
     273        out.writeBytes(boundary);
     274        out.writeBytes(NEWLINE);
     275        // write content header
     276        out.writeBytes("Content-Disposition: form-data; name=\"" + name +
     277                "\"; filename=\"" + fileName + "\"");
     278        out.writeBytes(NEWLINE);
     279        if(mimeType != null) {
     280            out.writeBytes("Content-Type: " + mimeType);
     281            out.writeBytes(NEWLINE);
     282        }
     283        out.writeBytes(NEWLINE);
     284        // write content
     285        byte[] data = new byte[1024];
     286        int r = 0;
     287        while((r = is.read(data, 0, data.length)) != -1) {
     288            out.write(data, 0, r);
     289        }
     290        // close input stream, but ignore any possible exception for it
     291        try {
     292            is.close();
     293        } catch(Exception e) {}
     294        out.writeBytes(NEWLINE);
     295        out.flush();
     296    }
     297
     298    /**
     299     * Writes the given bytes.  The bytes are assumed to be the contents
     300     * of a file, and will be sent as such.  If the data is null, a
     301     * <code>java.lang.IllegalArgumentException</code> will be thrown.
     302     *
     303    * @param  name      the field name
     304    * @param  mimeType  the file content type (optional, recommended)
     305    * @param  fileName  the file name (required)
     306    * @param  data      the file data
     307    * @throws  java.io.IOException  on input/output errors
     308    */
     309    public void writeFile(String name, String mimeType,
     310            String fileName, byte[] data)
     311    throws java.io.IOException {
     312        if(data == null) {
     313            throw new IllegalArgumentException("Data cannot be null.");
     314        }
     315        if(fileName == null || fileName.length() == 0) {
     316            throw new IllegalArgumentException("File name cannot be null or empty.");
     317        }
     318        /*
     319            --boundary\r\n
     320            Content-Disposition: form-data; name="<fieldName>"; filename="<filename>"\r\n
     321            Content-Type: <mime-type>\r\n
     322            \r\n
     323            <file-data>\r\n
     324        */
     325        // write boundary
     326        out.writeBytes(PREFIX);
     327        out.writeBytes(boundary);
     328        out.writeBytes(NEWLINE);
     329        // write content header
     330        out.writeBytes("Content-Disposition: form-data; name=\"" + name +
     331                "\"; filename=\"" + fileName + "\"");
     332        out.writeBytes(NEWLINE);
     333        if(mimeType != null) {
     334            out.writeBytes("Content-Type: " + mimeType);
     335            out.writeBytes(NEWLINE);
     336        }
     337        out.writeBytes(NEWLINE);
     338        // write content
     339        out.write(data, 0, data.length);
     340        out.writeBytes(NEWLINE);
     341        out.flush();
     342    }
     343
     344    /**
     345     * Flushes the stream.  Actually, this method does nothing, as the only
     346     * write methods are highly specialized and automatically flush.
     347    */
     348    public void flush() {
     349        // out.flush();
     350    }
     351
     352    /**
     353    * Closes the stream.  <br/>
     354    * <br/>
     355     * <b>NOTE:</b> This method <b>MUST</b> be called to finalize the
     356    * multipart stream.
     357     *
     358    * @throws  java.io.IOException  on input/output errors
     359    */
     360    public void close() throws java.io.IOException {
     361        // write final boundary
     362        out.writeBytes(PREFIX);
     363        out.writeBytes(boundary);
     364        out.writeBytes(PREFIX);
     365        out.writeBytes(NEWLINE);
     366        out.flush();
     367        out.close();
     368    }
     369
     370    /**
     371     * Gets the multipart boundary string being used by this stream.
     372     *
     373    * @return  the boundary
     374    */
     375    public String getBoundary() {
     376        return this.boundary;
     377    }
     378
     379    /**
     380     * Creates a new <code>java.net.URLConnection</code> object from the
     381     * specified <code>java.net.URL</code>.  This is a convenience method
     382     * which will set the <code>doInput</code>, <code>doOutput</code>,
     383     * <code>useCaches</code> and <code>defaultUseCaches</code> fields to
     384     * the appropriate settings in the correct order.
     385     *
     386    * @return  a <code>java.net.URLConnection</code> object for the URL
     387    * @throws  java.io.IOException  on input/output errors
     388    */
     389    public static URLConnection createConnection(URL url)
     390    throws java.io.IOException {
     391        URLConnection urlConn = url.openConnection();
     392        if(urlConn instanceof HttpURLConnection) {
     393            HttpURLConnection httpConn = (HttpURLConnection)urlConn;
     394            httpConn.setRequestMethod("POST");
     395        }
     396        urlConn.setDoInput(true);
     397        urlConn.setDoOutput(true);
     398        urlConn.setUseCaches(false);
     399        urlConn.setDefaultUseCaches(false);
     400        return urlConn;
     401    }
     402
     403    /**
     404     * Creates a multipart boundary string by concatenating 20 hyphens (-)
     405     * and the hexadecimal (base-16) representation of the current time in
     406     * milliseconds.
     407     *
     408    * @return  a multipart boundary string
     409    * @see  #getContentType(String)
     410    */
     411    public static String createBoundary() {
     412        return "--------------------" +
     413        Long.toString(System.currentTimeMillis(), 16);
     414    }
     415
     416    /**
     417     * Gets the content type string suitable for the
     418     * <code>java.net.URLConnection</code> which includes the multipart
     419    * boundary string.  <br/>
     420    * <br/>
     421     * This method is static because, due to the nature of the
     422     * <code>java.net.URLConnection</code> class, once the output stream
     423     * for the connection is acquired, it's too late to set the content
     424     * type (or any other request parameter).  So one has to create a
     425     * multipart boundary string first before using this class, such as
     426     * with the <code>createBoundary()</code> method.
     427     *
     428    * @param  boundary  the boundary string
     429    * @return  the content type string
     430    * @see  #createBoundary()
     431    */
     432    public static String getContentType(String boundary) {
     433        return "multipart/form-data; boundary=" + boundary;
     434    }
    435435}
  • trunk/src/org/openstreetmap/josm/io/MyHttpHandler.java

    r655 r1169  
    2626                proxyPort = port;
    2727            }
    28  
     28
    2929            protected java.net.URLConnection openConnection(URL u, Proxy p)
    3030                    throws IOException {
  • trunk/src/org/openstreetmap/josm/io/NmeaReader.java

    r1167 r1169  
    2828public class NmeaReader {
    2929
    30         /** Handler for the different types that NMEA speaks. */
    31         public static enum NMEA_TYPE {
    32 
    33                 /** RMC = recommended minimum sentence C. */
    34                 GPRMC("$GPRMC"),
    35                 /** GPS positions. */
    36                 GPGGA("$GPGGA"),
    37                 /** SA = satellites active. */
    38                 GPGSA("$GPGSA"),
    39                 /** Course over ground and ground speed */
    40                 GPVTG("$GPVTG");
    41 
    42                 private final String type;
    43 
    44                 NMEA_TYPE(String type) {
    45                         this.type = type;
    46                 }
    47 
    48                 public String getType() {
    49                         return this.type;
    50                 }
    51 
    52                 public boolean equals(String type) {
    53                         return this.type.equals(type);
    54                 }
    55         }
    56 
    57         // GPVTG
    58         public static enum GPVTG {
    59                 COURSE(1),COURSE_REF(2), // true course
    60                 COURSE_M(3), COURSE_M_REF(4), // magnetic course
    61                 SPEED_KN(5), SPEED_KN_UNIT(6), // speed in knots
    62                 SPEED_KMH(7), SPEED_KMH_UNIT(8), // speed in km/h
    63                 REST(9); // version-specific rest
    64 
    65                 public final int position;
    66 
    67                 GPVTG(int position) {
    68                         this.position = position;
    69                 }
    70         }
    71 
    72         // The following only applies to GPRMC
    73         public static enum GPRMC {
    74                 TIME(1),
    75                 /** Warning from the receiver (A = data ok, V = warning) */
    76                 RECEIVER_WARNING(2),
    77                 WIDTH_NORTH(3), WIDTH_NORTH_NAME(4), // Latitude, NS
    78                 LENGTH_EAST(5), LENGTH_EAST_NAME(6), // Longitude, EW
    79                 SPEED(7), COURSE(8), DATE(9),           // Speed in knots
    80                 MAGNETIC_DECLINATION(10), UNKNOWN(11),  // magnetic declination
    81                 /**
    82                 * Mode (A = autonom; D = differential; E = estimated; N = not valid; S
    83                 * = simulated)
    84                 *
    85                 * @since NMEA 2.3
    86                 */
    87                 MODE(12);
    88 
    89                 public final int position;
    90 
    91                 GPRMC(int position) {
    92                         this.position = position;
    93                 }
    94         }
    95 
    96         // The following only applies to GPGGA
    97         public static enum GPGGA {
    98                 TIME(1), LATITUDE(2), LATITUDE_NAME(3), LONGITUDE(4), LONGITUDE_NAME(5),
    99                 /**
    100                 * Quality (0 = invalid, 1 = GPS, 2 = DGPS, 6 = estimanted (@since NMEA
    101                 * 2.3))
    102                 */
    103                 QUALITY(6), SATELLITE_COUNT(7),
    104                 HDOP(8), // HDOP (horizontal dilution of precision)
    105                 HEIGHT(9), HEIGHT_UNTIS(10), // height above NN (above geoid)
    106                 HEIGHT_2(11), HEIGHT_2_UNTIS(12), // height geoid - height ellipsoid (WGS84)
    107                 GPS_AGE(13),// Age of differential GPS data
    108                 REF(14); // REF station
    109 
    110                 public final int position;
    111                 GPGGA(int position) {
    112                         this.position = position;
    113                 }
    114         }
    115 
    116         public static enum GPGSA {
    117                 AUTOMATIC(1),
    118                 FIX_TYPE(2), // 1 = not fixed, 2 = 2D fixed, 3 = 3D fixed)
    119                 // PRN numbers for max 12 satellites
    120                 PRN_1(3), PRN_2(4), PRN_3(5), PRN_4(6), PRN_5(7), PRN_6(8),
    121                 PRN_7(9), PRN_8(10), PRN_9(11), PRN_10(12), PRN_11(13), PRN_12(14),
    122                 PDOP(15),   // PDOP (precision)
    123                 HDOP(16),   // HDOP (horizontal precision)
    124                 VDOP(17), ; // VDOP (vertical precision)
    125 
    126                 public final int position;
    127                 GPGSA(int position) {
    128                         this.position = position;
    129                 }
    130         }
    131 
    132         public GpxData data;
     30    /** Handler for the different types that NMEA speaks. */
     31    public static enum NMEA_TYPE {
     32
     33        /** RMC = recommended minimum sentence C. */
     34        GPRMC("$GPRMC"),
     35        /** GPS positions. */
     36        GPGGA("$GPGGA"),
     37        /** SA = satellites active. */
     38        GPGSA("$GPGSA"),
     39        /** Course over ground and ground speed */
     40        GPVTG("$GPVTG");
     41
     42        private final String type;
     43
     44        NMEA_TYPE(String type) {
     45            this.type = type;
     46        }
     47
     48        public String getType() {
     49            return this.type;
     50        }
     51
     52        public boolean equals(String type) {
     53            return this.type.equals(type);
     54        }
     55    }
     56
     57    // GPVTG
     58    public static enum GPVTG {
     59        COURSE(1),COURSE_REF(2), // true course
     60        COURSE_M(3), COURSE_M_REF(4), // magnetic course
     61        SPEED_KN(5), SPEED_KN_UNIT(6), // speed in knots
     62        SPEED_KMH(7), SPEED_KMH_UNIT(8), // speed in km/h
     63        REST(9); // version-specific rest
     64
     65        public final int position;
     66
     67        GPVTG(int position) {
     68            this.position = position;
     69        }
     70    }
     71
     72    // The following only applies to GPRMC
     73    public static enum GPRMC {
     74        TIME(1),
     75        /** Warning from the receiver (A = data ok, V = warning) */
     76        RECEIVER_WARNING(2),
     77        WIDTH_NORTH(3), WIDTH_NORTH_NAME(4), // Latitude, NS
     78        LENGTH_EAST(5), LENGTH_EAST_NAME(6), // Longitude, EW
     79        SPEED(7), COURSE(8), DATE(9),           // Speed in knots
     80        MAGNETIC_DECLINATION(10), UNKNOWN(11),  // magnetic declination
     81        /**
     82        * Mode (A = autonom; D = differential; E = estimated; N = not valid; S
     83        * = simulated)
     84        *
     85        * @since NMEA 2.3
     86        */
     87        MODE(12);
     88
     89        public final int position;
     90
     91        GPRMC(int position) {
     92            this.position = position;
     93        }
     94    }
     95
     96    // The following only applies to GPGGA
     97    public static enum GPGGA {
     98        TIME(1), LATITUDE(2), LATITUDE_NAME(3), LONGITUDE(4), LONGITUDE_NAME(5),
     99        /**
     100        * Quality (0 = invalid, 1 = GPS, 2 = DGPS, 6 = estimanted (@since NMEA
     101        * 2.3))
     102        */
     103        QUALITY(6), SATELLITE_COUNT(7),
     104        HDOP(8), // HDOP (horizontal dilution of precision)
     105        HEIGHT(9), HEIGHT_UNTIS(10), // height above NN (above geoid)
     106        HEIGHT_2(11), HEIGHT_2_UNTIS(12), // height geoid - height ellipsoid (WGS84)
     107        GPS_AGE(13),// Age of differential GPS data
     108        REF(14); // REF station
     109
     110        public final int position;
     111        GPGGA(int position) {
     112            this.position = position;
     113        }
     114    }
     115
     116    public static enum GPGSA {
     117        AUTOMATIC(1),
     118        FIX_TYPE(2), // 1 = not fixed, 2 = 2D fixed, 3 = 3D fixed)
     119        // PRN numbers for max 12 satellites
     120        PRN_1(3), PRN_2(4), PRN_3(5), PRN_4(6), PRN_5(7), PRN_6(8),
     121        PRN_7(9), PRN_8(10), PRN_9(11), PRN_10(12), PRN_11(13), PRN_12(14),
     122        PDOP(15),   // PDOP (precision)
     123        HDOP(16),   // HDOP (horizontal precision)
     124        VDOP(17), ; // VDOP (vertical precision)
     125
     126        public final int position;
     127        GPGSA(int position) {
     128            this.position = position;
     129        }
     130    }
     131
     132    public GpxData data;
    133133
    134134//  private final static SimpleDateFormat GGATIMEFMT =
    135135//      new SimpleDateFormat("HHmmss.SSS");
    136         private final static SimpleDateFormat RMCTIMEFMT =
    137                 new SimpleDateFormat("ddMMyyHHmmss.SSS");
    138 
    139         // functons for reading the error stats
    140         public NMEAParserState ps;
    141 
    142         public int getParserUnknown() {
    143                 return ps.unknown;
    144         }
    145         public int getParserZeroCoordinates() {
    146                 return ps.zero_coord;
    147         }
    148         public int getParserChecksumErrors() {
    149                 return ps.checksum_errors;
    150         }
    151         public int getParserMalformed() {
    152                 return ps.malformed;
    153         }
    154         public int getNumberOfCoordinates() {
    155                 return ps.success;
    156         }
    157 
    158         public NmeaReader(InputStream source, File relativeMarkerPath) {
    159 
    160                 // create the data tree
    161                 data = new GpxData();
    162                 GpxTrack currentTrack = new GpxTrack();
    163                 data.tracks.add(currentTrack);
    164 
    165                 try {
    166                         BufferedReader rd =
    167                                 new BufferedReader(new InputStreamReader(source));
    168 
    169                         StringBuffer sb = new StringBuffer(1024);
    170                         int loopstart_char = rd.read();
    171                         if(loopstart_char == -1) {// zero size file
    172                                 //TODO tell user about the problem?
    173                                 return;
    174                         }
    175                         sb.append((char)loopstart_char);
    176                         ps = new NMEAParserState();
    177                         ps.p_Date="010100"; // TODO date problem
    178                         while(true) {
    179                                 // don't load unparsable files completely to memory
    180                                 if(sb.length()>=1020) sb.delete(0, sb.length()-1);
    181                                 int c = rd.read();
    182                                 if(c=='$') {
    183                                         ParseNMEASentence(sb.toString(), ps);
    184                                         sb.delete(0, sb.length());
    185                                         sb.append('$');
    186                                 } else if(c == -1) {
    187                                         // EOF: add last WayPoint if it works out
    188                                         ParseNMEASentence(sb.toString(),ps);
    189                                         break;
    190                                 } else sb.append((char)c);
    191                         }
    192                         rd.close();
    193                         Object[] wparr = ps.waypoints.toArray();
    194                         currentTrack.trackSegs.add(ps.waypoints);
    195                         data.recalculateBounds();
    196 
    197                 } catch (final IOException e) {
    198                         // TODO tell user about the problem?
    199                 }
    200         }
    201         private class NMEAParserState {
    202                 protected Collection<WayPoint> waypoints = new ArrayList<WayPoint>();
    203                 protected String p_Time;
    204                 protected String p_Date;
    205                 protected WayPoint p_Wp;
    206 
    207                 protected int success = 0; // number of successfully parsend sentences
    208                 protected int malformed = 0;
    209                 protected int checksum_errors = 0;
    210                 protected int unknown = 0;
    211                 protected int zero_coord = 0;
    212         }
    213 
    214         // Parses split up sentences into WayPoints which are stored
    215         // in the collection in the NMEAParserState object.
    216         // Returns true if the input made sence, false otherwise.
    217         private boolean ParseNMEASentence(String s, NMEAParserState ps) {
    218                 try {
    219                         if(s.equals("")) throw(null);
    220 
    221                         // checksum check:
    222                         // the bytes between the $ and the * are xored;
    223                         // if there is no * or other meanities it will throw
    224                         // and result in a malformed packet.
    225                         String[] chkstrings = s.split("\\*");
    226                         byte[] chb = chkstrings[0].getBytes();
    227                         int chk=0;
    228                         for(int i = 1; i < chb.length; i++) chk ^= chb[i];
    229                         if(Integer.parseInt(chkstrings[1].substring(0,2),16) != chk) {
    230                                 //System.out.println("Checksum error");
    231                                 ps.checksum_errors++;
    232                                 ps.p_Wp=null;
    233                                 return false;
    234                         }
    235                         // now for the content
    236                         String[] e = chkstrings[0].split(",");
    237                         String accu;
    238 
    239                         WayPoint currentwp = ps.p_Wp;
    240                         String currentDate = ps.p_Date;
    241 
    242                         // handle the packet content
    243                         if(e[0].equals("$GPGGA")) {
    244                                 // Position
    245                                 LatLon latLon = parseLatLon(
    246                                                 e[GPGGA.LATITUDE_NAME.position],
    247                                                 e[GPGGA.LONGITUDE_NAME.position],
    248                                                 e[GPGGA.LATITUDE.position],
    249                                                 e[GPGGA.LONGITUDE.position]
    250                                         );
    251                                 if(latLon==null) throw(null); // malformed
    252 
    253                                 if((latLon.lat()==0.0) && (latLon.lon()==0.0)) {
    254                                         ps.zero_coord++;
    255                                         return false;
    256                                 }
    257 
    258                                 // time
    259                                 accu = e[GPGGA.TIME.position];
    260                                 Date d = RMCTIMEFMT.parse(currentDate+accu, new ParsePosition(0));
    261                                 if (d == null) throw(null); // malformed
    262 
    263                                 if((ps.p_Time==null) || (currentwp==null) || !ps.p_Time.equals(accu)) {
    264                                         // this node is newer than the previous, create a new waypoint.
    265                                         // no matter if previous WayPoint was null, we got something
    266                                         // better now.
    267                                         ps.p_Time=accu;
    268                                         currentwp = new WayPoint(latLon);
    269                                 }
    270                                 if(!currentwp.attr.containsKey("time")) {
    271                                         // As this sentence has no complete time only use it
    272                                         // if there is no time so far
    273                                         String gpxdate = WayPoint.GPXTIMEFMT.format(d);
    274                                         currentwp.attr.put("time", gpxdate);
    275                                 }
    276                                 // elevation
    277                                 accu=e[GPGGA.HEIGHT_UNTIS.position];
    278                                 if(accu.equals("M")) {
    279                                                 // Ignore heights that are not in meters for now
    280                                                 accu=e[GPGGA.HEIGHT.position];
    281                                                 if(!accu.equals("")) {
    282                                                 Double.parseDouble(accu);
    283                                                 // if it throws it's malformed; this should only happen if the
    284                                                 // device sends nonstandard data.
    285                                                 if(!accu.equals("")) currentwp.attr.put("ele", accu);
    286                                         }
    287                                 }
    288                                 // number of sattelites
    289                                 accu=e[GPGGA.SATELLITE_COUNT.position];
    290                                 int sat = 0;
    291                                 if(!accu.equals("")) {
    292                                         sat = Integer.parseInt(accu);
    293                                         currentwp.attr.put("sat", accu);
    294                                 }
    295                                 // h-dilution
    296                                 accu=e[GPGGA.HDOP.position];
    297                                 if(!accu.equals("")) {
    298                                         Double.parseDouble(accu);
    299                                         currentwp.attr.put("hdop", accu);
    300                                 }
    301                                 // fix
    302                                 accu=e[GPGGA.QUALITY.position];
    303                                 if(!accu.equals("")) {
    304                                         int fixtype = Integer.parseInt(accu);
    305                                         switch(fixtype) {
    306                                         case 0:
    307                                                 currentwp.attr.put("fix", "none");
    308                                                 break;
    309                                         case 1:
    310                                                 if(sat < 4) currentwp.attr.put("fix", "2d");
    311                                                 else currentwp.attr.put("fix", "3d");
    312                                                 break;
    313                                         case 2:
    314                                                 currentwp.attr.put("fix", "dgps");
    315                                                 break;
    316                                         default:
    317                                                 break;
    318                                         }
    319                                 }
    320                         } else if(e[0].equals("$GPVTG")) {
    321                                 // COURSE
    322                                 accu = e[GPVTG.COURSE_REF.position];
    323                                 if(accu.equals("T")) {
    324                                         // other values than (T)rue are ignored
    325                                         accu = e[GPVTG.COURSE.position];
    326                                         if(!accu.equals("")) {
    327                                                 Double.parseDouble(accu);
    328                                                 currentwp.attr.put("course", accu);
    329                                         }
    330                                 }
    331                                 // SPEED
    332                                 accu = e[GPVTG.SPEED_KMH_UNIT.position];
    333                                 if(accu.startsWith("K")) {
    334                                         accu = e[GPVTG.SPEED_KMH.position];
    335                                         if(!accu.equals("")) {
    336                                                 double speed = Double.parseDouble(accu);
    337                                                 speed /= 3.6; // speed in m/s
    338                                                 currentwp.attr.put("speed", Double.toString(speed));
    339                                         }
    340                                 }
    341                         } else if(e[0].equals("$GPGSA")) {
    342                                 // vdop
    343                                 accu=e[GPGSA.VDOP.position];
    344                                 if(!accu.equals("")) {
    345                                         Double.parseDouble(accu);
    346                                         currentwp.attr.put("vdop", accu);
    347                                 }
    348                                 // hdop
    349                                 accu=e[GPGSA.HDOP.position];
    350                                 if(!accu.equals("")) {
    351                                         Double.parseDouble(accu);
    352                                         currentwp.attr.put("hdop", accu);
    353                                 }
    354                                 // pdop
    355                                 accu=e[GPGSA.PDOP.position];
    356                                 if(!accu.equals("")) {
    357                                         Double.parseDouble(accu);
    358                                         currentwp.attr.put("pdop", accu);
    359                                 }
    360                         }
    361                         else if(e[0].equals("$GPRMC")) {
    362                                 // coordinates
    363                                 LatLon latLon = parseLatLon(
    364                                                 e[GPRMC.WIDTH_NORTH_NAME.position],
    365                                                 e[GPRMC.LENGTH_EAST_NAME.position],
    366                                                 e[GPRMC.WIDTH_NORTH.position],
    367                                                 e[GPRMC.LENGTH_EAST.position]
    368                                         );
    369                                 if(latLon==null) throw(null);
    370                                 if((latLon.lat()==0.0) && (latLon.lon()==0.0)) {
    371                                         ps.zero_coord++;
    372                                         return false;
    373                                 }
    374                                 // time
    375                                 currentDate = e[GPRMC.DATE.position];
    376                                 String time = e[GPRMC.TIME.position];
    377 
    378                                 Date d = RMCTIMEFMT.parse(currentDate+time, new ParsePosition(0));
    379                                 if (d == null) throw(null);
    380 
    381                                 if((ps.p_Time==null) || (currentwp==null) || !ps.p_Time.equals(time)) {
    382                                         // this node is newer than the previous, create a new waypoint.
    383                                         ps.p_Time=time;
    384                                         currentwp = new WayPoint(latLon);
    385                                 }
    386                                 // time: this sentence has complete time so always use it.
    387                                 String gpxdate = WayPoint.GPXTIMEFMT.format(d);
    388                                 currentwp.attr.put("time", gpxdate);
    389                                 // speed
    390                                 accu = e[GPRMC.SPEED.position];
    391                                 if(!accu.equals("") && !currentwp.attr.containsKey("speed")) {
    392                                         double speed = Double.parseDouble(accu);
    393                                         speed *= 0.514444444; // to m/s
    394                                         currentwp.attr.put("speed", Double.toString(speed));
    395                                 }
    396                                 // course
    397                                 accu = e[GPRMC.COURSE.position];
    398                                 if(!accu.equals("") && !currentwp.attr.containsKey("course")) {
    399                                         Double.parseDouble(accu);
    400                                         currentwp.attr.put("course", accu);
    401                                 }
    402 
    403                                 // TODO fix?
    404                                 // * Mode (A = autonom; D = differential; E = estimated; N = not valid; S
    405                                 // * = simulated)
    406                                 // *
    407                                 // * @since NMEA 2.3
    408                                 //
    409                                 //MODE(12);
    410                         } else {
    411                                 ps.unknown++;
    412                                 return false;
    413                         }
    414                         ps.p_Date = currentDate;
    415                         if(ps.p_Wp != currentwp) {
    416                                 if(ps.p_Wp!=null) {
    417                                         ps.p_Wp.setTime();
    418                                 }
    419                                 ps.p_Wp = currentwp;
    420                                 ps.waypoints.add(currentwp);
    421                                 ps.success++;
    422                                 return true;
    423                         }
    424                         return true;
    425 
    426                 } catch(Exception x) {
    427                         // out of bounds and such
    428                         //System.out.println("Malformed line: "+s.toString().trim());
    429                         ps.malformed++;
    430                         ps.p_Wp=null;
    431                         return false;
    432                 }
    433         }
    434 
    435         private LatLon parseLatLon(String ew, String ns, String dlat, String dlon)
    436                 throws NumberFormatException {
    437                 String widthNorth = dlat.trim();
    438                 String lengthEast = dlon.trim();
    439 
    440                 // return a zero latlon instead of null so it is logged as zero coordinate
    441                 // instead of malformed sentence
    442                 if(widthNorth.equals("")&&lengthEast.equals("")) return new LatLon(0.0,0.0);
    443 
    444                 // The format is xxDDLL.LLLL
    445                 // xx optional whitespace
    446                 // DD (int) degres
    447                 // LL.LLLL (double) latidude
    448                 int latdegsep = widthNorth.indexOf('.') - 2;
    449                 if (latdegsep < 0) return null;
    450 
    451                 int latdeg = Integer.parseInt(widthNorth.substring(0, latdegsep));
    452                 double latmin = Double.parseDouble(widthNorth.substring(latdegsep));
    453                 double lat = latdeg + latmin / 60;
    454                 if ("S".equals(ns)) {
    455                         if(!ew.equals("N")) return null;
    456                         lat = -lat;
    457                 }
    458 
    459                 int londegsep = lengthEast.indexOf('.') - 2;
    460                 if (londegsep < 0) return null;
    461 
    462                 int londeg = Integer.parseInt(lengthEast.substring(0, londegsep));
    463                 double lonmin = Double.parseDouble(lengthEast.substring(londegsep));
    464                 double lon = londeg + lonmin / 60;
    465                 if ("W".equals(ew)) {
    466                         if(!ew.equals("E")) return null;
    467                         lon = -lon;
    468                 }
    469                 return new LatLon(lat, lon);
    470         }
     136    private final static SimpleDateFormat RMCTIMEFMT =
     137        new SimpleDateFormat("ddMMyyHHmmss.SSS");
     138
     139    // functons for reading the error stats
     140    public NMEAParserState ps;
     141
     142    public int getParserUnknown() {
     143        return ps.unknown;
     144    }
     145    public int getParserZeroCoordinates() {
     146        return ps.zero_coord;
     147    }
     148    public int getParserChecksumErrors() {
     149        return ps.checksum_errors;
     150    }
     151    public int getParserMalformed() {
     152        return ps.malformed;
     153    }
     154    public int getNumberOfCoordinates() {
     155        return ps.success;
     156    }
     157
     158    public NmeaReader(InputStream source, File relativeMarkerPath) {
     159
     160        // create the data tree
     161        data = new GpxData();
     162        GpxTrack currentTrack = new GpxTrack();
     163        data.tracks.add(currentTrack);
     164
     165        try {
     166            BufferedReader rd =
     167                new BufferedReader(new InputStreamReader(source));
     168
     169            StringBuffer sb = new StringBuffer(1024);
     170            int loopstart_char = rd.read();
     171            if(loopstart_char == -1) {// zero size file
     172                //TODO tell user about the problem?
     173                return;
     174            }
     175            sb.append((char)loopstart_char);
     176            ps = new NMEAParserState();
     177            ps.p_Date="010100"; // TODO date problem
     178            while(true) {
     179                // don't load unparsable files completely to memory
     180                if(sb.length()>=1020) sb.delete(0, sb.length()-1);
     181                int c = rd.read();
     182                if(c=='$') {
     183                    ParseNMEASentence(sb.toString(), ps);
     184                    sb.delete(0, sb.length());
     185                    sb.append('$');
     186                } else if(c == -1) {
     187                    // EOF: add last WayPoint if it works out
     188                    ParseNMEASentence(sb.toString(),ps);
     189                    break;
     190                } else sb.append((char)c);
     191            }
     192            rd.close();
     193            Object[] wparr = ps.waypoints.toArray();
     194            currentTrack.trackSegs.add(ps.waypoints);
     195            data.recalculateBounds();
     196
     197        } catch (final IOException e) {
     198            // TODO tell user about the problem?
     199        }
     200    }
     201    private class NMEAParserState {
     202        protected Collection<WayPoint> waypoints = new ArrayList<WayPoint>();
     203        protected String p_Time;
     204        protected String p_Date;
     205        protected WayPoint p_Wp;
     206
     207        protected int success = 0; // number of successfully parsend sentences
     208        protected int malformed = 0;
     209        protected int checksum_errors = 0;
     210        protected int unknown = 0;
     211        protected int zero_coord = 0;
     212    }
     213
     214    // Parses split up sentences into WayPoints which are stored
     215    // in the collection in the NMEAParserState object.
     216    // Returns true if the input made sence, false otherwise.
     217    private boolean ParseNMEASentence(String s, NMEAParserState ps) {
     218        try {
     219            if(s.equals("")) throw(null);
     220
     221            // checksum check:
     222            // the bytes between the $ and the * are xored;
     223            // if there is no * or other meanities it will throw
     224            // and result in a malformed packet.
     225            String[] chkstrings = s.split("\\*");
     226            byte[] chb = chkstrings[0].getBytes();
     227            int chk=0;
     228            for(int i = 1; i < chb.length; i++) chk ^= chb[i];
     229            if(Integer.parseInt(chkstrings[1].substring(0,2),16) != chk) {
     230                //System.out.println("Checksum error");
     231                ps.checksum_errors++;
     232                ps.p_Wp=null;
     233                return false;
     234            }
     235            // now for the content
     236            String[] e = chkstrings[0].split(",");
     237            String accu;
     238
     239            WayPoint currentwp = ps.p_Wp;
     240            String currentDate = ps.p_Date;
     241
     242            // handle the packet content
     243            if(e[0].equals("$GPGGA")) {
     244                // Position
     245                LatLon latLon = parseLatLon(
     246                        e[GPGGA.LATITUDE_NAME.position],
     247                        e[GPGGA.LONGITUDE_NAME.position],
     248                        e[GPGGA.LATITUDE.position],
     249                        e[GPGGA.LONGITUDE.position]
     250                    );
     251                if(latLon==null) throw(null); // malformed
     252
     253                if((latLon.lat()==0.0) && (latLon.lon()==0.0)) {
     254                    ps.zero_coord++;
     255                    return false;
     256                }
     257
     258                // time
     259                accu = e[GPGGA.TIME.position];
     260                Date d = RMCTIMEFMT.parse(currentDate+accu, new ParsePosition(0));
     261                if (d == null) throw(null); // malformed
     262
     263                if((ps.p_Time==null) || (currentwp==null) || !ps.p_Time.equals(accu)) {
     264                    // this node is newer than the previous, create a new waypoint.
     265                    // no matter if previous WayPoint was null, we got something
     266                    // better now.
     267                    ps.p_Time=accu;
     268                    currentwp = new WayPoint(latLon);
     269                }
     270                if(!currentwp.attr.containsKey("time")) {
     271                    // As this sentence has no complete time only use it
     272                    // if there is no time so far
     273                    String gpxdate = WayPoint.GPXTIMEFMT.format(d);
     274                    currentwp.attr.put("time", gpxdate);
     275                }
     276                // elevation
     277                accu=e[GPGGA.HEIGHT_UNTIS.position];
     278                if(accu.equals("M")) {
     279                        // Ignore heights that are not in meters for now
     280                        accu=e[GPGGA.HEIGHT.position];
     281                        if(!accu.equals("")) {
     282                        Double.parseDouble(accu);
     283                        // if it throws it's malformed; this should only happen if the
     284                        // device sends nonstandard data.
     285                        if(!accu.equals("")) currentwp.attr.put("ele", accu);
     286                    }
     287                }
     288                // number of sattelites
     289                accu=e[GPGGA.SATELLITE_COUNT.position];
     290                int sat = 0;
     291                if(!accu.equals("")) {
     292                    sat = Integer.parseInt(accu);
     293                    currentwp.attr.put("sat", accu);
     294                }
     295                // h-dilution
     296                accu=e[GPGGA.HDOP.position];
     297                if(!accu.equals("")) {
     298                    Double.parseDouble(accu);
     299                    currentwp.attr.put("hdop", accu);
     300                }
     301                // fix
     302                accu=e[GPGGA.QUALITY.position];
     303                if(!accu.equals("")) {
     304                    int fixtype = Integer.parseInt(accu);
     305                    switch(fixtype) {
     306                    case 0:
     307                        currentwp.attr.put("fix", "none");
     308                        break;
     309                    case 1:
     310                        if(sat < 4) currentwp.attr.put("fix", "2d");
     311                        else currentwp.attr.put("fix", "3d");
     312                        break;
     313                    case 2:
     314                        currentwp.attr.put("fix", "dgps");
     315                        break;
     316                    default:
     317                        break;
     318                    }
     319                }
     320            } else if(e[0].equals("$GPVTG")) {
     321                // COURSE
     322                accu = e[GPVTG.COURSE_REF.position];
     323                if(accu.equals("T")) {
     324                    // other values than (T)rue are ignored
     325                    accu = e[GPVTG.COURSE.position];
     326                    if(!accu.equals("")) {
     327                        Double.parseDouble(accu);
     328                        currentwp.attr.put("course", accu);
     329                    }
     330                }
     331                // SPEED
     332                accu = e[GPVTG.SPEED_KMH_UNIT.position];
     333                if(accu.startsWith("K")) {
     334                    accu = e[GPVTG.SPEED_KMH.position];
     335                    if(!accu.equals("")) {
     336                        double speed = Double.parseDouble(accu);
     337                        speed /= 3.6; // speed in m/s
     338                        currentwp.attr.put("speed", Double.toString(speed));
     339                    }
     340                }
     341            } else if(e[0].equals("$GPGSA")) {
     342                // vdop
     343                accu=e[GPGSA.VDOP.position];
     344                if(!accu.equals("")) {
     345                    Double.parseDouble(accu);
     346                    currentwp.attr.put("vdop", accu);
     347                }
     348                // hdop
     349                accu=e[GPGSA.HDOP.position];
     350                if(!accu.equals("")) {
     351                    Double.parseDouble(accu);
     352                    currentwp.attr.put("hdop", accu);
     353                }
     354                // pdop
     355                accu=e[GPGSA.PDOP.position];
     356                if(!accu.equals("")) {
     357                    Double.parseDouble(accu);
     358                    currentwp.attr.put("pdop", accu);
     359                }
     360            }
     361            else if(e[0].equals("$GPRMC")) {
     362                // coordinates
     363                LatLon latLon = parseLatLon(
     364                        e[GPRMC.WIDTH_NORTH_NAME.position],
     365                        e[GPRMC.LENGTH_EAST_NAME.position],
     366                        e[GPRMC.WIDTH_NORTH.position],
     367                        e[GPRMC.LENGTH_EAST.position]
     368                    );
     369                if(latLon==null) throw(null);
     370                if((latLon.lat()==0.0) && (latLon.lon()==0.0)) {
     371                    ps.zero_coord++;
     372                    return false;
     373                }
     374                // time
     375                currentDate = e[GPRMC.DATE.position];
     376                String time = e[GPRMC.TIME.position];
     377
     378                Date d = RMCTIMEFMT.parse(currentDate+time, new ParsePosition(0));
     379                if (d == null) throw(null);
     380
     381                if((ps.p_Time==null) || (currentwp==null) || !ps.p_Time.equals(time)) {
     382                    // this node is newer than the previous, create a new waypoint.
     383                    ps.p_Time=time;
     384                    currentwp = new WayPoint(latLon);
     385                }
     386                // time: this sentence has complete time so always use it.
     387                String gpxdate = WayPoint.GPXTIMEFMT.format(d);
     388                currentwp.attr.put("time", gpxdate);
     389                // speed
     390                accu = e[GPRMC.SPEED.position];
     391                if(!accu.equals("") && !currentwp.attr.containsKey("speed")) {
     392                    double speed = Double.parseDouble(accu);
     393                    speed *= 0.514444444; // to m/s
     394                    currentwp.attr.put("speed", Double.toString(speed));
     395                }
     396                // course
     397                accu = e[GPRMC.COURSE.position];
     398                if(!accu.equals("") && !currentwp.attr.containsKey("course")) {
     399                    Double.parseDouble(accu);
     400                    currentwp.attr.put("course", accu);
     401                }
     402
     403                // TODO fix?
     404                // * Mode (A = autonom; D = differential; E = estimated; N = not valid; S
     405                // * = simulated)
     406                // *
     407                // * @since NMEA 2.3
     408                //
     409                //MODE(12);
     410            } else {
     411                ps.unknown++;
     412                return false;
     413            }
     414            ps.p_Date = currentDate;
     415            if(ps.p_Wp != currentwp) {
     416                if(ps.p_Wp!=null) {
     417                    ps.p_Wp.setTime();
     418                }
     419                ps.p_Wp = currentwp;
     420                ps.waypoints.add(currentwp);
     421                ps.success++;
     422                return true;
     423            }
     424            return true;
     425
     426        } catch(Exception x) {
     427            // out of bounds and such
     428            //System.out.println("Malformed line: "+s.toString().trim());
     429            ps.malformed++;
     430            ps.p_Wp=null;
     431            return false;
     432        }
     433    }
     434
     435    private LatLon parseLatLon(String ew, String ns, String dlat, String dlon)
     436        throws NumberFormatException {
     437        String widthNorth = dlat.trim();
     438        String lengthEast = dlon.trim();
     439
     440        // return a zero latlon instead of null so it is logged as zero coordinate
     441        // instead of malformed sentence
     442        if(widthNorth.equals("")&&lengthEast.equals("")) return new LatLon(0.0,0.0);
     443
     444        // The format is xxDDLL.LLLL
     445        // xx optional whitespace
     446        // DD (int) degres
     447        // LL.LLLL (double) latidude
     448        int latdegsep = widthNorth.indexOf('.') - 2;
     449        if (latdegsep < 0) return null;
     450
     451        int latdeg = Integer.parseInt(widthNorth.substring(0, latdegsep));
     452        double latmin = Double.parseDouble(widthNorth.substring(latdegsep));
     453        double lat = latdeg + latmin / 60;
     454        if ("S".equals(ns)) {
     455            if(!ew.equals("N")) return null;
     456            lat = -lat;
     457        }
     458
     459        int londegsep = lengthEast.indexOf('.') - 2;
     460        if (londegsep < 0) return null;
     461
     462        int londeg = Integer.parseInt(lengthEast.substring(0, londegsep));
     463        double lonmin = Double.parseDouble(lengthEast.substring(londegsep));
     464        double lon = londeg + lonmin / 60;
     465        if ("W".equals(ew)) {
     466            if(!ew.equals("E")) return null;
     467            lon = -lon;
     468        }
     469        return new LatLon(lat, lon);
     470    }
    471471}
  • trunk/src/org/openstreetmap/josm/io/OsmConnection.java

    r763 r1169  
    3434public class OsmConnection {
    3535
    36         public static class OsmParseException extends Exception {
    37                 public OsmParseException() {super();}
    38                 public OsmParseException(String message, Throwable cause) {super(message, cause);}
    39                 public OsmParseException(String message) {super(message);}
    40                 public OsmParseException(Throwable cause) {super(cause);}
    41         }
     36    public static class OsmParseException extends Exception {
     37        public OsmParseException() {super();}
     38        public OsmParseException(String message, Throwable cause) {super(message, cause);}
     39        public OsmParseException(String message) {super(message);}
     40        public OsmParseException(Throwable cause) {super(cause);}
     41    }
    4242
    43         protected boolean cancel = false;
    44         protected HttpURLConnection activeConnection;
     43    protected boolean cancel = false;
     44    protected HttpURLConnection activeConnection;
    4545
    46         private static OsmAuth authentication = new OsmAuth();
    47         /**
    48         * Initialize the http defaults and the authenticator.
    49         */
    50         static {
    51                 //TODO: refactor this crap (maybe just insert the damn auth http-header by yourself)
    52                 try {
    53                 HttpURLConnection.setFollowRedirects(true);
    54                 Authenticator.setDefault(authentication);
     46    private static OsmAuth authentication = new OsmAuth();
     47    /**
     48    * Initialize the http defaults and the authenticator.
     49    */
     50    static {
     51        //TODO: refactor this crap (maybe just insert the damn auth http-header by yourself)
     52        try {
     53            HttpURLConnection.setFollowRedirects(true);
     54            Authenticator.setDefault(authentication);
    5555        } catch (SecurityException e) {
    5656        }
    57         }
     57    }
    5858
    59         /**
    60         * The authentication class handling the login requests.
    61         */
    62         private static class OsmAuth extends Authenticator {
    63                 /**
    64                 * Set to true, when the autenticator tried the password once.
    65                 */
    66                 boolean passwordtried = false;
    67                 /**
    68                 * Whether the user cancelled the password dialog
    69                 */
    70                 boolean authCancelled = false;
     59    /**
     60    * The authentication class handling the login requests.
     61    */
     62    private static class OsmAuth extends Authenticator {
     63        /**
     64        * Set to true, when the autenticator tried the password once.
     65        */
     66        boolean passwordtried = false;
     67        /**
     68        * Whether the user cancelled the password dialog
     69        */
     70        boolean authCancelled = false;
    7171
    72                 @Override protected PasswordAuthentication getPasswordAuthentication() {
    73                         String username = Main.pref.get("osm-server.username");
    74                         String password = Main.pref.get("osm-server.password");
    75                         if (passwordtried || username.equals("") || password.equals("")) {
    76                                 JPanel p = new JPanel(new GridBagLayout());
    77                                 if (!username.equals("") && !password.equals(""))
    78                                         p.add(new JLabel(tr("Incorrect password or username.")), GBC.eop());
    79                                 p.add(new JLabel(tr("Username")), GBC.std().insets(0,0,10,0));
    80                                 JTextField usernameField = new JTextField(username, 20);
    81                                 p.add(usernameField, GBC.eol());
    82                                 p.add(new JLabel(tr("Password")), GBC.std().insets(0,0,10,0));
    83                                 JPasswordField passwordField = new JPasswordField(password, 20);
    84                                 p.add(passwordField, GBC.eol());
    85                                 JLabel warning = new JLabel(tr("Warning: The password is transferred unencrypted."));
    86                                 warning.setFont(warning.getFont().deriveFont(Font.ITALIC));
    87                                 p.add(warning, GBC.eop());
     72        @Override protected PasswordAuthentication getPasswordAuthentication() {
     73            String username = Main.pref.get("osm-server.username");
     74            String password = Main.pref.get("osm-server.password");
     75            if (passwordtried || username.equals("") || password.equals("")) {
     76                JPanel p = new JPanel(new GridBagLayout());
     77                if (!username.equals("") && !password.equals(""))
     78                    p.add(new JLabel(tr("Incorrect password or username.")), GBC.eop());
     79                p.add(new JLabel(tr("Username")), GBC.std().insets(0,0,10,0));
     80                JTextField usernameField = new JTextField(username, 20);
     81                p.add(usernameField, GBC.eol());
     82                p.add(new JLabel(tr("Password")), GBC.std().insets(0,0,10,0));
     83                JPasswordField passwordField = new JPasswordField(password, 20);
     84                p.add(passwordField, GBC.eol());
     85                JLabel warning = new JLabel(tr("Warning: The password is transferred unencrypted."));
     86                warning.setFont(warning.getFont().deriveFont(Font.ITALIC));
     87                p.add(warning, GBC.eop());
    8888
    89                                 JCheckBox savePassword = new JCheckBox(tr("Save user and password (unencrypted)"), !username.equals("") && !password.equals(""));
    90                                 p.add(savePassword, GBC.eop());
     89                JCheckBox savePassword = new JCheckBox(tr("Save user and password (unencrypted)"), !username.equals("") && !password.equals(""));
     90                p.add(savePassword, GBC.eop());
    9191
    92                                 int choice = JOptionPane.showConfirmDialog(Main.parent, p, tr("Enter Password"), JOptionPane.OK_CANCEL_OPTION);
    93                                 if (choice == JOptionPane.CANCEL_OPTION) {
    94                                         authCancelled = true;
    95                                         return null;
    96                                 }
    97                                 username = usernameField.getText();
    98                                 password = String.valueOf(passwordField.getPassword());
    99                                 if (savePassword.isSelected()) {
    100                                         Main.pref.put("osm-server.username", username);
    101                                         Main.pref.put("osm-server.password", password);
    102                                 }
    103                                 if (username.equals(""))
    104                                         return null;
    105                         }
    106                         passwordtried = true;
    107                         return new PasswordAuthentication(username, password.toCharArray());
    108                 }
    109         }
     92                int choice = JOptionPane.showConfirmDialog(Main.parent, p, tr("Enter Password"), JOptionPane.OK_CANCEL_OPTION);
     93                if (choice == JOptionPane.CANCEL_OPTION) {
     94                    authCancelled = true;
     95                    return null;
     96                }
     97                username = usernameField.getText();
     98                password = String.valueOf(passwordField.getPassword());
     99                if (savePassword.isSelected()) {
     100                    Main.pref.put("osm-server.username", username);
     101                    Main.pref.put("osm-server.password", password);
     102                }
     103                if (username.equals(""))
     104                    return null;
     105            }
     106            passwordtried = true;
     107            return new PasswordAuthentication(username, password.toCharArray());
     108        }
     109    }
    110110
    111         /**
    112         * Must be called before each connection attemp to initialize the authentication.
    113         */
    114         protected final void initAuthentication() {
    115                 authentication.authCancelled = false;
    116                 authentication.passwordtried = false;
    117         }
     111    /**
     112    * Must be called before each connection attemp to initialize the authentication.
     113    */
     114    protected final void initAuthentication() {
     115        authentication.authCancelled = false;
     116        authentication.passwordtried = false;
     117    }
    118118
    119         /**
    120         * @return Whether the connection was cancelled.
    121         */
    122         protected final boolean isAuthCancelled() {
    123                 return authentication.authCancelled;
    124         }
     119    /**
     120    * @return Whether the connection was cancelled.
     121    */
     122    protected final boolean isAuthCancelled() {
     123        return authentication.authCancelled;
     124    }
    125125
    126         public void cancel() {
    127                 Main.pleaseWaitDlg.currentAction.setText(tr("Aborting..."));
    128                 cancel = true;
    129                 if (activeConnection != null) {
    130                         activeConnection.setConnectTimeout(100);
    131                         activeConnection.setReadTimeout(100);
    132                         try {
    133                                 Thread.sleep(100);
    134                         } catch (InterruptedException ex) {}
    135                         activeConnection.disconnect();
    136                 }
    137         }
     126    public void cancel() {
     127        Main.pleaseWaitDlg.currentAction.setText(tr("Aborting..."));
     128        cancel = true;
     129        if (activeConnection != null) {
     130            activeConnection.setConnectTimeout(100);
     131            activeConnection.setReadTimeout(100);
     132            try {
     133                Thread.sleep(100);
     134            } catch (InterruptedException ex) {}
     135            activeConnection.disconnect();
     136        }
     137    }
    138138
    139         protected void addAuth(HttpURLConnection con) throws CharacterCodingException {
    140                 CharsetEncoder encoder = Charset.forName("UTF-8").newEncoder();
    141                 String auth = Main.pref.get("osm-server.username") + ":" + Main.pref.get("osm-server.password");
    142                 ByteBuffer bytes = encoder.encode(CharBuffer.wrap(auth));
    143                 con.addRequestProperty("Authorization", "Basic "+Base64.encode(bytes));
    144         }
     139    protected void addAuth(HttpURLConnection con) throws CharacterCodingException {
     140        CharsetEncoder encoder = Charset.forName("UTF-8").newEncoder();
     141        String auth = Main.pref.get("osm-server.username") + ":" + Main.pref.get("osm-server.password");
     142        ByteBuffer bytes = encoder.encode(CharBuffer.wrap(auth));
     143        con.addRequestProperty("Authorization", "Basic "+Base64.encode(bytes));
     144    }
    145145}
  • trunk/src/org/openstreetmap/josm/io/OsmIdReader.java

    r627 r1169  
    2121/**
    2222 * Read only the ids and classes of an stream.
    23  * 
     23 *
    2424 * @author Imi
    2525 */
    2626public class OsmIdReader extends DefaultHandler {
    2727
    28         private boolean cancel;
    29         Map<Long, String> entries = new HashMap<Long, String>();
    30         private Reader in;
     28    private boolean cancel;
     29    Map<Long, String> entries = new HashMap<Long, String>();
     30    private Reader in;
    3131
    32         @Override public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException {
    33                 if (qName.equals("node") || qName.equals("way") || qName.equals("relation")) {
    34                         try {
    35                                 entries.put(Long.valueOf(atts.getValue("id")), qName);
    36                         } catch (Exception e) {
    37                                 e.printStackTrace();
    38                                 throw new SAXException(tr("Error during parse."));
    39                         }
    40                 }
     32    @Override public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException {
     33        if (qName.equals("node") || qName.equals("way") || qName.equals("relation")) {
     34            try {
     35                entries.put(Long.valueOf(atts.getValue("id")), qName);
     36            } catch (Exception e) {
     37                e.printStackTrace();
     38                throw new SAXException(tr("Error during parse."));
     39            }
     40        }
    4141    }
    4242
    43         public Map<Long, String> parseIds(InputStream in) throws IOException, SAXException {
     43    public Map<Long, String> parseIds(InputStream in) throws IOException, SAXException {
    4444        this.in = new InputStreamReader(in, "UTF-8");
    45                 try {
    46                 SAXParserFactory.newInstance().newSAXParser().parse(new InputSource(this.in), this);
     45        try {
     46            SAXParserFactory.newInstance().newSAXParser().parse(new InputSource(this.in), this);
    4747        } catch (ParserConfigurationException e) {
    48                 if (!cancel) {
    49                         e.printStackTrace(); // broken SAXException chaining
    50                         throw new SAXException(e);
    51                 }
     48            if (!cancel) {
     49                e.printStackTrace(); // broken SAXException chaining
     50                throw new SAXException(e);
     51            }
    5252        } catch (SAXException e) {
    53                 if (!cancel)
    54                         throw e;
    55         }       
    56                 return entries;
    57         }
    58        
    59         public void cancel() {
    60                 cancel = true;
    61                 if (in != null)
     53            if (!cancel)
     54                throw e;
     55        }
     56        return entries;
     57    }
     58
     59    public void cancel() {
     60        cancel = true;
     61        if (in != null)
    6262            try {in.close();} catch (IOException e) {}
    63         }
     63    }
    6464}
  • trunk/src/org/openstreetmap/josm/io/OsmReader.java

    r1071 r1169  
    106106     private Map<OsmPrimitiveData, Collection<Long>> ways = new HashMap<OsmPrimitiveData, Collection<Long>>();
    107107
    108      /** 
     108     /**
    109109      * Data structure for relation objects
    110110      */
    111111     private Map<OsmPrimitiveData, Collection<RelationMemberData>> relations = new HashMap<OsmPrimitiveData, Collection<RelationMemberData>>();
    112112
    113      /** 
     113     /**
    114114      * List of protocol versions that will be accepted on reading
    115115      */
     
    133133                         generator = atts.getValue("generator");
    134134
    135                          
     135
    136136                    } else if (qName.equals("bound")) {
    137137                         // old style bounds.
    138138                         // TODO: remove this around 1st October 2008.
    139                          // - this is a bit of a hack; since we still write out old style bound objects, 
     139                         // - this is a bit of a hack; since we still write out old style bound objects,
    140140                         // we don't want to load them both. so when writing, we add a "note" tag the our
    141141                         // old-style bound, and when reading, ignore those with a "note".
     
    172172                              ds.dataSources.add(src);
    173173                         }
    174                          
     174
    175175                    // ---- PARSING NODES AND WAYS ----
    176                          
     176
    177177                    } else if (qName.equals("node")) {
    178178                         current = new Node(new LatLon(getDouble(atts, "lat"), getDouble(atts, "lon")));
     
    192192                         list.add(id);
    193193
    194                     // ---- PARSING RELATIONS ----               
     194                    // ---- PARSING RELATIONS ----
    195195
    196196                    } else if (qName.equals("relation")) {
     
    207207                         emd.type=atts.getValue("type");
    208208                         emd.relationMember.role = atts.getValue("role");
    209                          
     209
    210210                         if (emd.id == 0)
    211211                              throw new SAXException(tr("Incomplete <member> specification with ref=0"));
    212                          
     212
    213213                         list.add(emd);
    214                          
     214
    215215                    // ---- PARSING TAGS (applicable to all objects) ----
    216                          
     216
    217217                    } else if (qName.equals("tag")) {
    218218                         current.put(atts.getValue("k"), atts.getValue("v"));
     
    231231          }
    232232     }
    233      
    234      /** 
     233
     234     /**
    235235      * Constructor initializes list of allowed protocol versions.
    236236      */
     
    239239          allowedVersions.add(Main.pref.get("osm-server.version", "0.5"));
    240240          // now also add all compatible versions
    241           String[] additionalVersions = 
     241          String[] additionalVersions =
    242242               Main.pref.get("osm-server.additional-versions", "").split("/,/");
    243243          if (additionalVersions.length == 1 && additionalVersions[0].length() == 0)
    244244               additionalVersions = new String[] {};
    245           allowedVersions.addAll(Arrays.asList(additionalVersions));     
     245          allowedVersions.addAll(Arrays.asList(additionalVersions));
    246246     }
    247247
     
    267267               current.timestamp = time;
    268268          }
    269          
     269
    270270          // user attribute added in 0.4 API
    271271          String user = atts.getValue("user");
     
    274274               current.user = User.get(user);
    275275          }
    276          
     276
    277277          // visible attribute added in 0.4 API
    278278          String visible = atts.getValue("visible");
     
    342342               adder.visit(w);
    343343          }
    344          
     344
    345345     }
    346346
     
    348348      * Return the Way object with the given id, or null if it doesn't
    349349      * exist yet. This method only looks at ways stored in the data set.
    350       * 
     350      *
    351351      * @param id
    352352      * @return way object or null
     
    365365      * Return the Relation object with the given id, or null if it doesn't
    366366      * exist yet. This method only looks at relations stored in the data set.
    367       * 
     367      *
    368368      * @param id
    369369      * @return relation object or null
     
    380380
    381381     /**
    382       * Create relations. This is slightly different than n/s/w because 
     382      * Create relations. This is slightly different than n/s/w because
    383383      * unlike other objects, relations may reference other relations; it
    384384      * is not guaranteed that a referenced relation will have been created
     
    387387      */
    388388     private void createRelations() {
    389          
     389
    390390          // pass 1 - create all relations
    391391          for (Entry<OsmPrimitiveData, Collection<RelationMemberData>> e : relations.entrySet()) {
     
    399399               Relation en = findRelation(e.getKey().id);
    400400               if (en == null) throw new Error("Failed to create relation " + e.getKey().id);
    401                
     401
    402402               for (RelationMemberData emd : e.getValue()) {
    403403                    RelationMember em = emd.relationMember;
  • trunk/src/org/openstreetmap/josm/io/OsmServerLocationReader.java

    r1146 r1169  
    1212
    1313public class OsmServerLocationReader extends OsmServerReader {
    14  
     14
    1515    String url;
    16    
     16
    1717    public OsmServerLocationReader(String url) {
    1818        this.url = url;
    1919    }
    20    
     20
    2121    /**
    2222     * Method to download OSM files from somewhere
  • trunk/src/org/openstreetmap/josm/io/OsmServerObjectReader.java

    r1146 r1169  
    2020    String type;
    2121    boolean full;
    22    
     22
    2323    public OsmServerObjectReader(long id, String type, boolean full) {
    2424        this.id = id;
  • trunk/src/org/openstreetmap/josm/io/OsmServerReader.java

    r1146 r1169  
    1919/**
    2020 * This DataReader reads directly from the REST API of the osm server.
    21  * 
     21 *
    2222 * It supports plain text transfer as well as gzip or deflate encoded transfers;
    2323 * if compressed transfers are unwanted, set property osm-server.use-compression
     
    2727 */
    2828public abstract class OsmServerReader extends OsmConnection {
    29         /**
    30         * Open a connection to the given url and return a reader on the input stream
    31         * from that connection. In case of user cancel, return <code>null</code>.
    32         * @param urlStr The exact url to connect to.
    33         * @param pleaseWaitDlg
    34         * @return An reader reading the input stream (servers answer) or <code>null</code>.
    35         */
    36         protected InputStream getInputStream(String urlStr, PleaseWaitDialog pleaseWaitDlg) throws IOException {
    37                 String version = Main.pref.get("osm-server.version", "0.5");
    38                 urlStr = Main.pref.get("osm-server.url")+"/"+version+"/" + urlStr;
     29    /**
     30    * Open a connection to the given url and return a reader on the input stream
     31    * from that connection. In case of user cancel, return <code>null</code>.
     32    * @param urlStr The exact url to connect to.
     33    * @param pleaseWaitDlg
     34    * @return An reader reading the input stream (servers answer) or <code>null</code>.
     35    */
     36    protected InputStream getInputStream(String urlStr, PleaseWaitDialog pleaseWaitDlg) throws IOException {
     37        String version = Main.pref.get("osm-server.version", "0.5");
     38        urlStr = Main.pref.get("osm-server.url")+"/"+version+"/" + urlStr;
    3939        return getInputStreamRaw(urlStr, pleaseWaitDlg);
    4040    }
    41    
     41
    4242    protected InputStream getInputStreamRaw(String urlStr, PleaseWaitDialog pleaseWaitDlg) throws IOException {
    43        
    44                 System.out.println("download: "+urlStr);
    45                 initAuthentication();
    46                 URL url = new URL(urlStr);
    47                 activeConnection = (HttpURLConnection)url.openConnection();
    48                 if (cancel) {
    49                         activeConnection.disconnect();
    50                         return null;
    51                 }
    52                
    53                 if (Boolean.parseBoolean(Main.pref.get("osm-server.use-compression", "true")))
    54                         activeConnection.setRequestProperty("Accept-Encoding", "gzip, deflate");
    5543
    56                 activeConnection.setConnectTimeout(15000);
    57                 if (isAuthCancelled() && activeConnection.getResponseCode() == 401)
    58                         return null;
    59                 if (activeConnection.getResponseCode() == 500)
    60                 {
    61                         throw new IOException(tr("Server returned internal error. Try a reduced area or retry after waiting some time."));
    62                 }
    63 //              System.out.println("got return: "+activeConnection.getResponseCode());
     44        System.out.println("download: "+urlStr);
     45        initAuthentication();
     46        URL url = new URL(urlStr);
     47        activeConnection = (HttpURLConnection)url.openConnection();
     48        if (cancel) {
     49            activeConnection.disconnect();
     50            return null;
     51        }
    6452
    65                 String encoding = activeConnection.getContentEncoding();
    66                 InputStream inputStream = new ProgressInputStream(activeConnection, pleaseWaitDlg);
    67                 if (encoding != null && encoding.equalsIgnoreCase("gzip")) {
    68                         inputStream = new GZIPInputStream(inputStream);
    69                 }
    70                 else if (encoding != null && encoding.equalsIgnoreCase("deflate")) {
    71                         inputStream = new InflaterInputStream(inputStream, new Inflater(true));
    72                 }
    73                 return inputStream;
    74         }
    75    
     53        if (Boolean.parseBoolean(Main.pref.get("osm-server.use-compression", "true")))
     54            activeConnection.setRequestProperty("Accept-Encoding", "gzip, deflate");
     55
     56        activeConnection.setConnectTimeout(15000);
     57        if (isAuthCancelled() && activeConnection.getResponseCode() == 401)
     58            return null;
     59        if (activeConnection.getResponseCode() == 500)
     60        {
     61            throw new IOException(tr("Server returned internal error. Try a reduced area or retry after waiting some time."));
     62        }
     63//      System.out.println("got return: "+activeConnection.getResponseCode());
     64
     65        String encoding = activeConnection.getContentEncoding();
     66        InputStream inputStream = new ProgressInputStream(activeConnection, pleaseWaitDlg);
     67        if (encoding != null && encoding.equalsIgnoreCase("gzip")) {
     68            inputStream = new GZIPInputStream(inputStream);
     69        }
     70        else if (encoding != null && encoding.equalsIgnoreCase("deflate")) {
     71            inputStream = new InflaterInputStream(inputStream, new Inflater(true));
     72        }
     73        return inputStream;
     74    }
     75
    7676    public abstract DataSet parseOsm() throws SAXException, IOException;
    77    
     77
    7878}
  • trunk/src/org/openstreetmap/josm/io/OsmServerWriter.java

    r1137 r1169  
    7979
    8080    long uploadStartTime;
    81    
     81
    8282    public String timeLeft(int progress, int list_size) {
    8383        long now = System.currentTimeMillis();
     
    9696        return time_left_str;
    9797    }
    98    
     98
    9999    public void uploadOsm(Collection<OsmPrimitive> list) throws SAXException {
    100100        processed = new LinkedList<OsmPrimitive>();
     
    103103        Main.pleaseWaitDlg.progress.setMaximum(list.size());
    104104        Main.pleaseWaitDlg.progress.setValue(0);
    105        
     105
    106106        // controls whether or not we open and close a changeset. API 0.6 requires changesets.
    107107        boolean useChangesets = Main.pref.get("osm-server.version", "0.5").equals("0.6");
    108        
     108
    109109        // controls whether or not we try and uplaod the whole bunch in one go
    110         boolean useDiffUploads = Main.pref.getBoolean("osm-server.atomic-upload", 
     110        boolean useDiffUploads = Main.pref.getBoolean("osm-server.atomic-upload",
    111111            Main.pref.get("osm-server.version", "0.5").equals("0.6"));
    112        
     112
    113113        String comment = null;
    114114        while (useChangesets && comment == null) {
    115             comment = JOptionPane.showInputDialog(Main.parent, 
     115            comment = JOptionPane.showInputDialog(Main.parent,
    116116                 tr("Provide a brief comment as to the changes to you are uploading:"),
    117117                 tr("Commit comment"), JOptionPane.QUESTION_MESSAGE);
     
    131131            return;
    132132        }
    133    
     133
    134134        try {
    135135            if (useDiffUploads) {
     
    166166            }
    167167            catch (OsmTransferException ex) {
    168                 dealWithTransferException(ex);   
     168                dealWithTransferException(ex);
    169169            }
    170170            dealWithTransferException(e);
    171171        }
    172172    }
    173    
     173
    174174    /* FIXME: This code is terrible, please fix it!!!! */
    175175
     
    183183     * can fix the issue where hitting cancel doesn't do anything while
    184184     * retrying. - Mv0 Apr 2008
    185      * 
     185     *
    186186     * Cancelling has an effect now, maybe it does not always catch on. Florian Heer, Aug 08
    187187     */
     
    198198                    Main.pref.get("osm-server.url") +
    199199                    "/" + version +
    200                     "/" + "changeset" + 
     200                    "/" + "changeset" +
    201201                    "/" + "create");
    202202            System.out.print("upload to: "+url+ "..." );
     
    205205            activeConnection.setRequestMethod("PUT");
    206206            addAuth(activeConnection);
    207            
     207
    208208            activeConnection.setDoOutput(true);
    209209            OutputStream out = activeConnection.getOutputStream();
    210210            OsmWriter.output(out, changeset);
    211211            out.close();
    212            
     212
    213213            activeConnection.connect();
    214214            System.out.println("connected");
     
    224224            }
    225225            if (retCode != 200 && retCode != 412) {
    226                
     226
    227227                if (retries >= 0) {
    228228                    retries--;
     
    232232                    return startChangeset(retries, comment);
    233233                }
    234                
     234
    235235                // Look for a detailed error message from the server
    236236                retMsg += "\n" + readString(activeConnection.getInputStream());
     
    262262                throw new OsmTransferException (e.getMessage()+ " " + e.getClass().getCanonicalName(), e);
    263263        }
    264        
     264
    265265        catch (Exception e) {
    266266            if (cancel)
     
    274274        return true;
    275275    }
    276    
     276
    277277    private void uploadDiff(int retries, Collection<OsmPrimitive> list) throws OsmTransferException {
    278        
     278
    279279        CreateOsmChangeVisitor duv = new CreateOsmChangeVisitor(changeset);
    280280
     
    290290        String diff = duv.getDocument();
    291291        System.out.println(diff);
    292        
     292
    293293        Main.pleaseWaitDlg.currentAction.setText(tr("Uploading..."));
    294294        try {
     
    299299                    Main.pref.get("osm-server.url") +
    300300                    "/" + version +
    301                     "/" + "changeset" + 
     301                    "/" + "changeset" +
    302302                    "/" + changeset.id +
    303303                    "/upload" );
     
    307307            activeConnection.setRequestMethod("POST");
    308308            addAuth(activeConnection);
    309            
     309
    310310            activeConnection.setDoOutput(true);
    311311            PrintWriter out;
     
    317317            out.print(diff);
    318318            out.close();
    319            
     319
    320320            activeConnection.connect();
    321321            System.out.println("connected");
     
    323323            int retCode = activeConnection.getResponseCode();
    324324            String retMsg = "";
    325            
     325
    326326            if (retCode == 200) {
    327327                DiffResultReader.parseDiffResult(activeConnection.getInputStream(), list, processed, duv.getNewIdMap(), Main.pleaseWaitDlg);
     
    333333                    System.out.println("retrying ("+retries+" left)");
    334334                    stopChangeset(retries);
    335                 } else { 
     335                } else {
    336336                    // Look for a detailed error message from the server
    337337                    retMsg += "\n" + readString(activeConnection.getInputStream());
     
    372372        }
    373373    }
    374    
    375    
     374
     375
    376376    private void stopChangeset(int retries) throws OsmTransferException {
    377377        Main.pleaseWaitDlg.currentAction.setText(tr("Closing changeset..."));
     
    383383                    Main.pref.get("osm-server.url") +
    384384                    "/" + version +
    385                     "/" + "changeset" + 
     385                    "/" + "changeset" +
    386386                    "/" + changeset.id +
    387387                    "/close" );
     
    391391            activeConnection.setRequestMethod("PUT");
    392392            addAuth(activeConnection);
    393            
     393
    394394            activeConnection.setDoOutput(true);
    395395            OutputStream out = activeConnection.getOutputStream();
    396396            OsmWriter.output(out, changeset);
    397397            out.close();
    398            
     398
    399399            activeConnection.connect();
    400400            System.out.println("connected");
     
    415415                    System.out.println("retrying ("+retries+" left)");
    416416                    stopChangeset(retries);
    417                 } else { 
     417                } else {
    418418                    // Look for a detailed error message from the server
    419419                    retMsg += readString(activeConnection.getInputStream());
    420                    
     420
    421421                    // Report our error
    422422                    ByteArrayOutputStream o = new ByteArrayOutputStream();
     
    490490        processed.add(e);
    491491    }
    492    
     492
    493493    /**
    494494     * Read a long from the input stream and return it.
     
    540540                    new URL(Main.pref.get("osm-server.url") +
    541541                    "/" + version + "/"),
    542                     urlSuffix + 
     542                    urlSuffix +
    543543                    "/" + (osm.id==0 ? "create" : osm.id),
    544544                    new MyHttpHandler());
     
    622622        }
    623623    }
    624    
     624
    625625    private void sendRequest(String requestMethod, String urlSuffix,
    626626            OsmPrimitive osm, boolean addBody)  {
     
    636636        }
    637637    }
    638    
     638
    639639    private void dealWithTransferException (OsmTransferException e) {
    640640        Main.pleaseWaitDlg.currentAction.setText(tr("Transfer aborted due to error (will wait for 5 seconds):") + e.getMessage());
  • trunk/src/org/openstreetmap/josm/io/OsmTransferException.java

    r784 r1169  
    44public class OsmTransferException extends Exception {
    55
    6         public OsmTransferException() {
    7         }
     6    public OsmTransferException() {
     7    }
    88
    9         public OsmTransferException(String message) {
    10                 super(message);
    11         }
     9    public OsmTransferException(String message) {
     10        super(message);
     11    }
    1212
    13         public OsmTransferException(Throwable cause) {
    14                 super(cause);
    15         }
     13    public OsmTransferException(Throwable cause) {
     14        super(cause);
     15    }
    1616
    17         public OsmTransferException(String message, Throwable cause) {
    18                 super(message, cause);
    19         }
     17    public OsmTransferException(String message, Throwable cause) {
     18        super(message, cause);
     19    }
    2020
    2121}
  • trunk/src/org/openstreetmap/josm/io/OsmWriter.java

    r1071 r1169  
    2424public class OsmWriter extends XmlWriter implements Visitor {
    2525
    26         /**
    27         * The counter for new created objects. Starting at -1 and goes down.
    28         */
    29         private long newIdCounter = -1;
    30         /**
    31         * All newly created ids and their primitive that uses it. This is a back reference
    32         * map to allow references to use the correnct primitives.
    33         */
    34         public HashMap<OsmPrimitive, Long> usedNewIds = new HashMap<OsmPrimitive, Long>();
    35 
    36         private final boolean osmConform;
    37         private final Changeset changeset;
    38 
    39         public abstract static class Osm implements OsmWriterInterface {
    40                 public void header(PrintWriter out) {
    41                         out.print("<osm version='");
    42                         out.print(Main.pref.get("osm-server.version", "0.5"));
    43                         out.println("' generator='JOSM'>");
    44                 }
    45                 public void footer(PrintWriter out) {
    46                         out.println("</osm>");
    47                 }
    48         }
    49        
    50         // simple helper to write the object's class to the out stream
    51         private Visitor typeWriteVisitor = new Visitor() {
    52                 public void visit(Node n) { out.print("node"); }
    53                 public void visit(Way w) { out.print("way"); }
    54                 public void visit(Relation e) { out.print("relation"); }
    55         };
    56        
    57         /**
    58         * An output writer for function output that writes everything of the given dataset into
    59         * the xml
    60         */
    61         public static final class All extends Osm {
    62                 private final DataSet ds;
    63                 private final boolean osmConform;
    64 
    65                 /**
    66                 * Construct an writer function
    67                 * @param osmConform <code>true</code>, if the xml should be 100% osm conform. In this
    68                  *              case, not all information can be retrieved later (as example, modified state
    69                  *              is lost and id's remain 0 instead of decrementing from -1)
    70                 */
    71                 public All(DataSet ds, boolean osmConform) {
    72                         this.ds = ds;
    73                         this.osmConform = osmConform;
    74                 }
    75 
    76                 public void write(PrintWriter out) {
    77                         Visitor writer = new OsmWriter(out, osmConform, null);
    78                         for (Node n : ds.nodes)
    79                                 if (shouldWrite(n))
    80                                         writer.visit(n);
    81                         for (Way w : ds.ways)
    82                                 if (shouldWrite(w))
    83                                         writer.visit(w);
    84                         for (Relation e : ds.relations)
    85                                 if (shouldWrite(e))
    86                                         writer.visit(e);
    87         }
    88 
    89                 private boolean shouldWrite(OsmPrimitive osm) {
    90                 return osm.id != 0 || !osm.deleted;
    91         }
    92 
    93                 @Override public void header(PrintWriter out) {
    94                 super.header(out);
    95                         for (DataSource s : ds.dataSources) {
     26    /**
     27    * The counter for new created objects. Starting at -1 and goes down.
     28    */
     29    private long newIdCounter = -1;
     30    /**
     31    * All newly created ids and their primitive that uses it. This is a back reference
     32    * map to allow references to use the correnct primitives.
     33    */
     34    public HashMap<OsmPrimitive, Long> usedNewIds = new HashMap<OsmPrimitive, Long>();
     35
     36    private final boolean osmConform;
     37    private final Changeset changeset;
     38
     39    public abstract static class Osm implements OsmWriterInterface {
     40        public void header(PrintWriter out) {
     41            out.print("<osm version='");
     42            out.print(Main.pref.get("osm-server.version", "0.5"));
     43            out.println("' generator='JOSM'>");
     44        }
     45        public void footer(PrintWriter out) {
     46            out.println("</osm>");
     47        }
     48    }
     49
     50    // simple helper to write the object's class to the out stream
     51    private Visitor typeWriteVisitor = new Visitor() {
     52        public void visit(Node n) { out.print("node"); }
     53        public void visit(Way w) { out.print("way"); }
     54        public void visit(Relation e) { out.print("relation"); }
     55    };
     56
     57    /**
     58    * An output writer for function output that writes everything of the given dataset into
     59    * the xml
     60    */
     61    public static final class All extends Osm {
     62        private final DataSet ds;
     63        private final boolean osmConform;
     64
     65        /**
     66        * Construct an writer function
     67        * @param osmConform <code>true</code>, if the xml should be 100% osm conform. In this
     68         *      case, not all information can be retrieved later (as example, modified state
     69         *      is lost and id's remain 0 instead of decrementing from -1)
     70        */
     71        public All(DataSet ds, boolean osmConform) {
     72            this.ds = ds;
     73            this.osmConform = osmConform;
     74        }
     75
     76        public void write(PrintWriter out) {
     77            Visitor writer = new OsmWriter(out, osmConform, null);
     78            for (Node n : ds.nodes)
     79                if (shouldWrite(n))
     80                    writer.visit(n);
     81            for (Way w : ds.ways)
     82                if (shouldWrite(w))
     83                    writer.visit(w);
     84            for (Relation e : ds.relations)
     85                if (shouldWrite(e))
     86                    writer.visit(e);
     87        }
     88
     89        private boolean shouldWrite(OsmPrimitive osm) {
     90            return osm.id != 0 || !osm.deleted;
     91        }
     92
     93        @Override public void header(PrintWriter out) {
     94            super.header(out);
     95            for (DataSource s : ds.dataSources) {
    9696                // TODO: remove <bound> output after a grace period (1st October 08)
    97                                 out.print("  <bound note='this tag is deprecated and only provided for backward compatiblity' box='"+
    98                                                 s.bounds.min.lat()+","+
    99                                                 s.bounds.min.lon()+","+
    100                                                 s.bounds.max.lat()+","+
    101                                                 s.bounds.max.lon()+"' ");
    102                                 out.println("origin='"+XmlWriter.encode(s.origin)+"' />");
    103                                 out.print("  <bounds minlat='" +
    104                                                 s.bounds.min.lat()+"' minlon='"+
    105                                                 s.bounds.min.lon()+"' maxlat='"+
    106                                                 s.bounds.max.lat()+"' maxlon='"+
    107                                                 s.bounds.max.lon()+"' ");
    108                                 out.println("origin='"+XmlWriter.encode(s.origin)+"' />");
    109                         }
    110         }
    111         }
    112 
    113         /**
    114         * An output writer for functino output that writes only one specific primitive into
    115         * the xml
    116         */
    117         public static final class Single extends Osm {
    118                 private final OsmPrimitive osm;
    119                 private final boolean osmConform;
    120                 private final Changeset changeset;
    121 
    122                 public Single(OsmPrimitive osm, boolean osmConform, Changeset changeset) {
    123                         this.osm = osm;
    124                         this.osmConform = osmConform;
    125                         this.changeset = changeset;
    126                 }
    127 
    128                 public void write(PrintWriter out) {
    129                         osm.visit(new OsmWriter(out, osmConform, changeset));
    130         }
    131         }
    132 
    133         public OsmWriter(PrintWriter out, boolean osmConform, Changeset changeset) {
    134                 super(out);
    135                 this.osmConform = osmConform;
    136                 this.changeset = changeset;
    137         }
    138 
    139         public void visit(Node n) {
    140                 if (n.incomplete) return;
    141                 addCommon(n, "node");
    142                 out.print(" lat='"+n.coor.lat()+"' lon='"+n.coor.lon()+"'");
    143                 addTags(n, "node", true);
    144         }
    145 
    146         public void visit(Way w) {
    147                 if (w.incomplete) return;
    148                 addCommon(w, "way");
    149                 out.println(">");
    150                 for (Node n : w.nodes)
    151                         out.println("    <nd ref='"+getUsedId(n)+"' />");
    152                 addTags(w, "way", false);
    153         }
    154 
    155         public void visit(Relation e) {
    156                 if (e.incomplete) return;
    157                 addCommon(e, "relation");
    158                 out.println(">");
    159                 for (RelationMember em : e.members) {
    160                         out.print("    <member type='");
    161                         em.member.visit(typeWriteVisitor);
    162                         out.println("' ref='"+getUsedId(em.member)+"' role='" +
    163                                 XmlWriter.encode(em.role) + "' />");
    164                 }
    165                 addTags(e, "relation", false);
    166         }
    167        
    168 
    169         /**
    170         * Return the id for the given osm primitive (may access the usedId map)
    171         */
    172         private long getUsedId(OsmPrimitive osm) {
    173                 if (osm.id != 0)
    174                         return osm.id;
    175                 if (usedNewIds.containsKey(osm))
    176                         return usedNewIds.get(osm);
    177                 usedNewIds.put(osm, newIdCounter);
    178                 return osmConform ? 0 : newIdCounter--;
    179         }
    180 
    181         private void addTags(OsmPrimitive osm, String tagname, boolean tagOpen) {
    182                 if (osm.keys != null) {
    183                         if (tagOpen)
    184                                 out.println(">");
    185                         for (Entry<String, String> e : osm.keys.entrySet())
    186                                 out.println("    <tag k='"+ XmlWriter.encode(e.getKey()) +
    187                                                 "' v='"+XmlWriter.encode(e.getValue())+ "' />");
    188                         out.println("  </" + tagname + ">");
    189                 } else if (tagOpen)
    190                         out.println(" />");
    191                 else
    192                         out.println("  </" + tagname + ">");
    193         }
    194 
    195         /**
    196         * Add the common part as the form of the tag as well as the XML attributes
    197         * id, action, user, and visible.
    198         */
    199         private void addCommon(OsmPrimitive osm, String tagname) {
    200                 out.print("  <"+tagname+" id='"+getUsedId(osm)+"'");
    201                 if (!osmConform) {
    202                         String action = null;
    203                         if (osm.deleted)
    204                                 action = "delete";
    205                         else if (osm.modified)
    206                                 action = "modify";
    207                         if (action != null)
    208                                 out.print(" action='"+action+"'");
    209                 }
    210                 if (osm.timestamp != null) {
    211                         out.print(" timestamp='"+osm.timestamp+"'");
    212                 }
    213                 // user and visible added with 0.4 API
    214                 if (osm.user != null) {
    215                         out.print(" user='"+XmlWriter.encode(osm.user.name)+"'");
    216                 }
    217                 out.print(" visible='"+osm.visible+"'");
    218                 if (osm.version != -1)
    219                         out.print(" version='"+osm.version+"'");
    220                 if (this.changeset != null && this.changeset.id != 0)
    221                         out.print(" changeset='"+this.changeset.id+"'" );
    222         }
     97                out.print("  <bound note='this tag is deprecated and only provided for backward compatiblity' box='"+
     98                        s.bounds.min.lat()+","+
     99                        s.bounds.min.lon()+","+
     100                        s.bounds.max.lat()+","+
     101                        s.bounds.max.lon()+"' ");
     102                out.println("origin='"+XmlWriter.encode(s.origin)+"' />");
     103                out.print("  <bounds minlat='" +
     104                        s.bounds.min.lat()+"' minlon='"+
     105                        s.bounds.min.lon()+"' maxlat='"+
     106                        s.bounds.max.lat()+"' maxlon='"+
     107                        s.bounds.max.lon()+"' ");
     108                out.println("origin='"+XmlWriter.encode(s.origin)+"' />");
     109            }
     110        }
     111    }
     112
     113    /**
     114    * An output writer for functino output that writes only one specific primitive into
     115    * the xml
     116    */
     117    public static final class Single extends Osm {
     118        private final OsmPrimitive osm;
     119        private final boolean osmConform;
     120        private final Changeset changeset;
     121
     122        public Single(OsmPrimitive osm, boolean osmConform, Changeset changeset) {
     123            this.osm = osm;
     124            this.osmConform = osmConform;
     125            this.changeset = changeset;
     126        }
     127
     128        public void write(PrintWriter out) {
     129            osm.visit(new OsmWriter(out, osmConform, changeset));
     130        }
     131    }
     132
     133    public OsmWriter(PrintWriter out, boolean osmConform, Changeset changeset) {
     134        super(out);
     135        this.osmConform = osmConform;
     136        this.changeset = changeset;
     137    }
     138
     139    public void visit(Node n) {
     140        if (n.incomplete) return;
     141        addCommon(n, "node");
     142        out.print(" lat='"+n.coor.lat()+"' lon='"+n.coor.lon()+"'");
     143        addTags(n, "node", true);
     144    }
     145
     146    public void visit(Way w) {
     147        if (w.incomplete) return;
     148        addCommon(w, "way");
     149        out.println(">");
     150        for (Node n : w.nodes)
     151            out.println("    <nd ref='"+getUsedId(n)+"' />");
     152        addTags(w, "way", false);
     153    }
     154
     155    public void visit(Relation e) {
     156        if (e.incomplete) return;
     157        addCommon(e, "relation");
     158        out.println(">");
     159        for (RelationMember em : e.members) {
     160            out.print("    <member type='");
     161            em.member.visit(typeWriteVisitor);
     162            out.println("' ref='"+getUsedId(em.member)+"' role='" +
     163                XmlWriter.encode(em.role) + "' />");
     164        }
     165        addTags(e, "relation", false);
     166    }
     167
     168
     169    /**
     170    * Return the id for the given osm primitive (may access the usedId map)
     171    */
     172    private long getUsedId(OsmPrimitive osm) {
     173        if (osm.id != 0)
     174            return osm.id;
     175        if (usedNewIds.containsKey(osm))
     176            return usedNewIds.get(osm);
     177        usedNewIds.put(osm, newIdCounter);
     178        return osmConform ? 0 : newIdCounter--;
     179    }
     180
     181    private void addTags(OsmPrimitive osm, String tagname, boolean tagOpen) {
     182        if (osm.keys != null) {
     183            if (tagOpen)
     184                out.println(">");
     185            for (Entry<String, String> e : osm.keys.entrySet())
     186                out.println("    <tag k='"+ XmlWriter.encode(e.getKey()) +
     187                        "' v='"+XmlWriter.encode(e.getValue())+ "' />");
     188            out.println("  </" + tagname + ">");
     189        } else if (tagOpen)
     190            out.println(" />");
     191        else
     192            out.println("  </" + tagname + ">");
     193    }
     194
     195    /**
     196    * Add the common part as the form of the tag as well as the XML attributes
     197    * id, action, user, and visible.
     198    */
     199    private void addCommon(OsmPrimitive osm, String tagname) {
     200        out.print("  <"+tagname+" id='"+getUsedId(osm)+"'");
     201        if (!osmConform) {
     202            String action = null;
     203            if (osm.deleted)
     204                action = "delete";
     205            else if (osm.modified)
     206                action = "modify";
     207            if (action != null)
     208                out.print(" action='"+action+"'");
     209        }
     210        if (osm.timestamp != null) {
     211            out.print(" timestamp='"+osm.timestamp+"'");
     212        }
     213        // user and visible added with 0.4 API
     214        if (osm.user != null) {
     215            out.print(" user='"+XmlWriter.encode(osm.user.name)+"'");
     216        }
     217        out.print(" visible='"+osm.visible+"'");
     218        if (osm.version != -1)
     219            out.print(" version='"+osm.version+"'");
     220        if (this.changeset != null && this.changeset.id != 0)
     221            out.print(" changeset='"+this.changeset.id+"'" );
     222    }
    223223}
  • trunk/src/org/openstreetmap/josm/io/ProgressInputStream.java

    r782 r1169  
    1616public class ProgressInputStream extends InputStream {
    1717
    18         private final InputStream in;
    19         private int readSoFar = 0;
    20         private int lastDialogUpdate = 0;
    21         private final URLConnection connection;
    22         private PleaseWaitDialog pleaseWaitDlg;
     18    private final InputStream in;
     19    private int readSoFar = 0;
     20    private int lastDialogUpdate = 0;
     21    private final URLConnection connection;
     22    private PleaseWaitDialog pleaseWaitDlg;
    2323
    24         public class OsmServerException extends IOException {
    25                 private OsmServerException(String e) {
    26                         super(e);
    27                 }
    28         }
     24    public class OsmServerException extends IOException {
     25        private OsmServerException(String e) {
     26            super(e);
     27        }
     28    }
    2929
    30         public ProgressInputStream(URLConnection con, PleaseWaitDialog pleaseWaitDlg) throws IOException, OsmServerException {
    31                 this.connection = con;
     30    public ProgressInputStream(URLConnection con, PleaseWaitDialog pleaseWaitDlg) throws IOException, OsmServerException {
     31        this.connection = con;
    3232
    33                 try {
    34                         this.in = con.getInputStream();
    35                 } catch (IOException e) {
    36                         if (con.getHeaderField("Error") != null)
    37                                 throw new OsmServerException(tr(con.getHeaderField("Error")));
    38                         throw e;
    39                 }
     33        try {
     34            this.in = con.getInputStream();
     35        } catch (IOException e) {
     36            if (con.getHeaderField("Error") != null)
     37                throw new OsmServerException(tr(con.getHeaderField("Error")));
     38            throw e;
     39        }
    4040
    41                 int contentLength = con.getContentLength();
    42                 this.pleaseWaitDlg = pleaseWaitDlg;
    43                 if (pleaseWaitDlg == null)
    44                         return;
    45                 if (contentLength > 0)
    46                         pleaseWaitDlg.progress.setMaximum(contentLength);
    47                 else
    48                         pleaseWaitDlg.progress.setMaximum(0);
    49                 pleaseWaitDlg.progress.setValue(0);
    50         }
     41        int contentLength = con.getContentLength();
     42        this.pleaseWaitDlg = pleaseWaitDlg;
     43        if (pleaseWaitDlg == null)
     44            return;
     45        if (contentLength > 0)
     46            pleaseWaitDlg.progress.setMaximum(contentLength);
     47        else
     48            pleaseWaitDlg.progress.setMaximum(0);
     49        pleaseWaitDlg.progress.setValue(0);
     50    }
    5151
    52         @Override public void close() throws IOException {
    53                 in.close();
    54         }
     52    @Override public void close() throws IOException {
     53        in.close();
     54    }
    5555
    56         @Override public int read(byte[] b, int off, int len) throws IOException {
    57                 int read = in.read(b, off, len);
    58                 if (read != -1)
    59                         advanceTicker(read);
    60                 return read;
    61         }
     56    @Override public int read(byte[] b, int off, int len) throws IOException {
     57        int read = in.read(b, off, len);
     58        if (read != -1)
     59            advanceTicker(read);
     60        return read;
     61    }
    6262
    63         @Override public int read() throws IOException {
    64                 int read = in.read();
    65                 if (read != -1)
    66                         advanceTicker(1);
    67                 return read;
    68         }
     63    @Override public int read() throws IOException {
     64        int read = in.read();
     65        if (read != -1)
     66            advanceTicker(1);
     67        return read;
     68    }
    6969
    70         /**
    71         * Increase ticker (progress counter and displayed text) by the given amount.
    72         * @param amount
    73         */
    74         private void advanceTicker(int amount) {
    75                 if (pleaseWaitDlg == null)
    76                         return;
     70    /**
     71    * Increase ticker (progress counter and displayed text) by the given amount.
     72    * @param amount
     73    */
     74    private void advanceTicker(int amount) {
     75        if (pleaseWaitDlg == null)
     76            return;
    7777
    78                 if (pleaseWaitDlg.progress.getMaximum() == 0 && connection.getContentLength() != -1)
    79                         pleaseWaitDlg.progress.setMaximum(connection.getContentLength());
     78        if (pleaseWaitDlg.progress.getMaximum() == 0 && connection.getContentLength() != -1)
     79            pleaseWaitDlg.progress.setMaximum(connection.getContentLength());
    8080
    81                 readSoFar += amount;
     81        readSoFar += amount;
    8282
    83                 if (readSoFar / 1024 != lastDialogUpdate) {
    84                         lastDialogUpdate++;
    85                         String progStr = " "+readSoFar/1024+"/";
    86                         progStr += (pleaseWaitDlg.progress.getMaximum()==0) ? "??? KB" : (pleaseWaitDlg.progress.getMaximum()/1024)+" KB";
    87                         pleaseWaitDlg.progress.setValue(readSoFar);
     83        if (readSoFar / 1024 != lastDialogUpdate) {
     84            lastDialogUpdate++;
     85            String progStr = " "+readSoFar/1024+"/";
     86            progStr += (pleaseWaitDlg.progress.getMaximum()==0) ? "??? KB" : (pleaseWaitDlg.progress.getMaximum()/1024)+" KB";
     87            pleaseWaitDlg.progress.setValue(readSoFar);
    8888
    89                         String cur = pleaseWaitDlg.currentAction.getText();
    90                         int i = cur.indexOf(' ');
    91                         if (i != -1)
    92                                 cur = cur.substring(0, i) + progStr;
    93                         else
    94                                 cur += progStr;
    95                         pleaseWaitDlg.currentAction.setText(cur);
    96                 }
    97         }
     89            String cur = pleaseWaitDlg.currentAction.getText();
     90            int i = cur.indexOf(' ');
     91            if (i != -1)
     92                cur = cur.substring(0, i) + progStr;
     93            else
     94                cur += progStr;
     95            pleaseWaitDlg.currentAction.setText(cur);
     96        }
     97    }
    9898}
  • trunk/src/org/openstreetmap/josm/io/XmlWriter.java

    r627 r1169  
    1010/**
    1111 * Helper class to use for xml outputting classes.
    12  * 
     12 *
    1313 * @author imi
    1414 */
    1515public class XmlWriter {
    1616
    17         /**
    18         * The interface to write the data into an Osm stream
    19         * @author immanuel.scholz
    20         */
    21         public static interface OsmWriterInterface {
    22                 void header(PrintWriter out);
    23                 void write(PrintWriter out);
    24                 void footer(PrintWriter out);
    25         }
     17    /**
     18    * The interface to write the data into an Osm stream
     19    * @author immanuel.scholz
     20    */
     21    public static interface OsmWriterInterface {
     22        void header(PrintWriter out);
     23        void write(PrintWriter out);
     24        void footer(PrintWriter out);
     25    }
    2626
    2727
    28         protected XmlWriter(PrintWriter out) {
    29                 this.out = out;
    30         }
     28    protected XmlWriter(PrintWriter out) {
     29        this.out = out;
     30    }
    3131
    32         /**
    33         * Encode the given string in XML1.0 format.
    34         * Optimized to fast pass strings that don't need encoding (normal case).
    35         */
    36         public static String encode(String unencoded) {
    37                 StringBuilder buffer = null;
    38                 for (int i = 0; i < unencoded.length(); ++i) {
    39                         String encS = XmlWriter.encoding.get(unencoded.charAt(i));
    40                         if (encS != null) {
    41                                 if (buffer == null)
    42                                         buffer = new StringBuilder(unencoded.substring(0,i));
    43                                 buffer.append(encS);
    44                         } else if (buffer != null)
    45                                 buffer.append(unencoded.charAt(i));
    46                 }
    47                 return (buffer == null) ? unencoded : buffer.toString();
    48         }
     32    /**
     33    * Encode the given string in XML1.0 format.
     34    * Optimized to fast pass strings that don't need encoding (normal case).
     35    */
     36    public static String encode(String unencoded) {
     37        StringBuilder buffer = null;
     38        for (int i = 0; i < unencoded.length(); ++i) {
     39            String encS = XmlWriter.encoding.get(unencoded.charAt(i));
     40            if (encS != null) {
     41                if (buffer == null)
     42                    buffer = new StringBuilder(unencoded.substring(0,i));
     43                buffer.append(encS);
     44            } else if (buffer != null)
     45                buffer.append(unencoded.charAt(i));
     46        }
     47        return (buffer == null) ? unencoded : buffer.toString();
     48    }
    4949
    50         /**
    51         * Write the header and start tag, then call the runnable to add all real tags and finally
    52         * "closes" the xml by writing the footer.
    53         */
    54         public static void output(OutputStream outStream, OsmWriterInterface outputWriter) {
    55                 PrintWriter out;
    56                 try {
    57                         out = new PrintWriter(new OutputStreamWriter(outStream, "UTF-8"));
    58                 } catch (UnsupportedEncodingException e) {
    59                         throw new RuntimeException(e);
    60                 }
    61                 out.println("<?xml version='1.0' encoding='UTF-8'?>");
    62                 outputWriter.header(out);
    63                 outputWriter.write(out);
    64                 outputWriter.footer(out);
    65                 out.flush();
    66                 out.close();
    67         }
     50    /**
     51    * Write the header and start tag, then call the runnable to add all real tags and finally
     52    * "closes" the xml by writing the footer.
     53    */
     54    public static void output(OutputStream outStream, OsmWriterInterface outputWriter) {
     55        PrintWriter out;
     56        try {
     57            out = new PrintWriter(new OutputStreamWriter(outStream, "UTF-8"));
     58        } catch (UnsupportedEncodingException e) {
     59            throw new RuntimeException(e);
     60        }
     61        out.println("<?xml version='1.0' encoding='UTF-8'?>");
     62        outputWriter.header(out);
     63        outputWriter.write(out);
     64        outputWriter.footer(out);
     65        out.flush();
     66        out.close();
     67    }
    6868
    69         /**
    70         * The output writer to save the values to.
    71         */
    72         protected final PrintWriter out;
    73         final private static HashMap<Character, String> encoding = new HashMap<Character, String>();
    74         static {
    75                 encoding.put('<', "&lt;");
    76                 encoding.put('>', "&gt;");
    77                 encoding.put('"', "&quot;");
    78                 encoding.put('\'', "&apos;");
    79                 encoding.put('&', "&amp;");
    80                 encoding.put('\n', "&#xA;");
    81                 encoding.put('\r', "&#xD;");
    82                 encoding.put('\t', "&#x9;");
    83         }
     69    /**
     70    * The output writer to save the values to.
     71    */
     72    protected final PrintWriter out;
     73    final private static HashMap<Character, String> encoding = new HashMap<Character, String>();
     74    static {
     75        encoding.put('<', "&lt;");
     76        encoding.put('>', "&gt;");
     77        encoding.put('"', "&quot;");
     78        encoding.put('\'', "&apos;");
     79        encoding.put('&', "&amp;");
     80        encoding.put('\n', "&#xA;");
     81        encoding.put('\r', "&#xD;");
     82        encoding.put('\t', "&#x9;");
     83    }
    8484}
  • trunk/src/org/openstreetmap/josm/plugins/Plugin.java

    r873 r1169  
    2424 *
    2525 * The actual implementation of this class is optional, as all functions will be called
    26  * via reflection. This is to be able to change this interface without the need of 
     26 * via reflection. This is to be able to change this interface without the need of
    2727 * recompiling or even breaking the plugins. If your class does not provide a
    2828 * function here (or does provide a function with a mismatching signature), it will not
     
    3232 * are provided (you can register yourself to more callbacks in your plugin class
    3333 * constructor).
    34  * 
     34 *
    3535 * Subclassing Plugin and overriding some functions makes it easy for you to keep sync
    3636 * with the correct actual plugin architecture of JOSM.
     
    4040public abstract class Plugin {
    4141
    42         /**
    43         * This is the info available for this plugin. You can access this from your
    44         * constructor.
    45         *
    46         * (The actual implementation to request the info from a static variable
    47         * is a bit hacky, but it works).
    48         */
    49         public final PluginInformation info = PluginInformation.currentPluginInitialization;
     42    /**
     43    * This is the info available for this plugin. You can access this from your
     44    * constructor.
     45    *
     46    * (The actual implementation to request the info from a static variable
     47    * is a bit hacky, but it works).
     48    */
     49    public final PluginInformation info = PluginInformation.currentPluginInitialization;
    5050
    51         /**
    52         * @return The directory for the plugin to store all kind of stuff.
    53         */
    54         public final String getPluginDir() {
    55                 return new File(Main.pref.getPluginsDirFile(), info.name).getPath();
    56         }
     51    /**
     52    * @return The directory for the plugin to store all kind of stuff.
     53    */
     54    public final String getPluginDir() {
     55        return new File(Main.pref.getPluginsDirFile(), info.name).getPath();
     56    }
    5757
    58         /**
    59         * Called after Main.mapFrame is initalized. (After the first data is loaded).
    60         * You can use this callback to tweak the newFrame to your needs, as example install
    61         * an alternative Painter.
    62         */
    63         public void mapFrameInitialized(MapFrame oldFrame, MapFrame newFrame) {}
     58    /**
     59    * Called after Main.mapFrame is initalized. (After the first data is loaded).
     60    * You can use this callback to tweak the newFrame to your needs, as example install
     61    * an alternative Painter.
     62    */
     63    public void mapFrameInitialized(MapFrame oldFrame, MapFrame newFrame) {}
    6464
    65         /**
    66         * Called in the preferences dialog to create a preferences page for the plugin,
    67         * if any available.
    68         */
    69         public PreferenceSetting getPreferenceSetting() { return null; }
    70        
    71         /**
    72         * Called in the download dialog to give the plugin a chance to modify the list
    73         * of bounding box selectors.
    74         */
    75         public void addDownloadSelection(List<DownloadSelection> list) {}
    76        
    77         /**
    78         * Copies the ressource 'from' to the file in the plugin directory named 'to'.
    79         */
    80         public void copy(String from, String to) throws FileNotFoundException, IOException {
    81                 String pluginDirName = Main.pref.getPreferencesDir()+"plugins/"+info.name+"/";
     65    /**
     66    * Called in the preferences dialog to create a preferences page for the plugin,
     67    * if any available.
     68    */
     69    public PreferenceSetting getPreferenceSetting() { return null; }
     70
     71    /**
     72    * Called in the download dialog to give the plugin a chance to modify the list
     73    * of bounding box selectors.
     74    */
     75    public void addDownloadSelection(List<DownloadSelection> list) {}
     76
     77    /**
     78    * Copies the ressource 'from' to the file in the plugin directory named 'to'.
     79    */
     80    public void copy(String from, String to) throws FileNotFoundException, IOException {
     81        String pluginDirName = Main.pref.getPreferencesDir()+"plugins/"+info.name+"/";
    8282        File pluginDir = new File(pluginDirName);
    8383        if (!pluginDir.exists())
    84                 pluginDir.mkdirs();
     84            pluginDir.mkdirs();
    8585        FileOutputStream out = new FileOutputStream(pluginDirName+to);
    8686        InputStream in = getClass().getResourceAsStream(from);
    8787        byte[] buffer = new byte[8192];
    8888        for(int len = in.read(buffer); len > 0; len = in.read(buffer))
    89                 out.write(buffer, 0, len);
     89            out.write(buffer, 0, len);
    9090        in.close();
    9191        out.close();
  • trunk/src/org/openstreetmap/josm/plugins/PluginDownloader.java

    r1073 r1169  
    11//License: GPL. Copyright 2007 by Immanuel Scholz and others
    22/**
    3  * 
     3 *
    44 */
    55package org.openstreetmap.josm.plugins;
     
    3434public class PluginDownloader {
    3535
    36         private static final class UpdateTask extends PleaseWaitRunnable {
    37                 private final Collection<PluginDescription> toUpdate;
    38                 private String errors = "";
    39                 private int count = 0;
     36    private static final class UpdateTask extends PleaseWaitRunnable {
     37        private final Collection<PluginDescription> toUpdate;
     38        private String errors = "";
     39        private int count = 0;
    4040
    41                 private UpdateTask(Collection<PluginDescription> toUpdate) {
    42                         super(tr("Update Plugins"));
    43                         this.toUpdate = toUpdate;
    44                 }
     41        private UpdateTask(Collection<PluginDescription> toUpdate) {
     42            super(tr("Update Plugins"));
     43            this.toUpdate = toUpdate;
     44        }
    4545
    46                 @Override protected void cancel() {
    47                         finish();
    48                 }
     46        @Override protected void cancel() {
     47            finish();
     48        }
    4949
    50                 @Override protected void finish() {
    51                         if (errors.length() > 0)
    52                                 JOptionPane.showMessageDialog(Main.parent, tr("There were problems with the following plugins:\n\n {0}",errors));
    53                         else
    54                                 JOptionPane.showMessageDialog(Main.parent, trn("{0} Plugin successfully updated. Please restart JOSM.", "{0} Plugins successfully updated. Please restart JOSM.", count, count));
    55                 }
     50        @Override protected void finish() {
     51            if (errors.length() > 0)
     52                JOptionPane.showMessageDialog(Main.parent, tr("There were problems with the following plugins:\n\n {0}",errors));
     53            else
     54                JOptionPane.showMessageDialog(Main.parent, trn("{0} Plugin successfully updated. Please restart JOSM.", "{0} Plugins successfully updated. Please restart JOSM.", count, count));
     55        }
    5656
    57                 @Override protected void realRun() throws SAXException, IOException {
    58                         File pluginDir = Main.pref.getPluginsDirFile();
    59                         if (!pluginDir.exists())
    60                                 pluginDir.mkdirs();
    61                         for (PluginDescription d : toUpdate) {
    62                                 File pluginFile = new File(pluginDir, d.name + ".jar.new");
    63                                 if (download(d.resource, pluginFile))
    64                                         count++;
    65                                 else
    66                                         errors += d.name + "\n";
    67                         }
    68                         PluginDownloader.moveUpdatedPlugins();
    69                 }
    70         }
     57        @Override protected void realRun() throws SAXException, IOException {
     58            File pluginDir = Main.pref.getPluginsDirFile();
     59            if (!pluginDir.exists())
     60                pluginDir.mkdirs();
     61            for (PluginDescription d : toUpdate) {
     62                File pluginFile = new File(pluginDir, d.name + ".jar.new");
     63                if (download(d.resource, pluginFile))
     64                    count++;
     65                else
     66                    errors += d.name + "\n";
     67            }
     68            PluginDownloader.moveUpdatedPlugins();
     69        }
     70    }
    7171
    72         private static final Pattern wiki = Pattern.compile("^</td></tr><tr><td><a class=\"ext-link\" href=\"([^\"]*)\"><span class=\"icon\">([^<]*)</span></a></td><td>([^<]*)</td><td>([^<].*)</td><td>(.*)");
     72    private static final Pattern wiki = Pattern.compile("^</td></tr><tr><td><a class=\"ext-link\" href=\"([^\"]*)\"><span class=\"icon\">([^<]*)</span></a></td><td>([^<]*)</td><td>([^<].*)</td><td>(.*)");
    7373
    74         private final static String[] pluginSites = {"http://josm.openstreetmap.de/wiki/Plugins"};
     74    private final static String[] pluginSites = {"http://josm.openstreetmap.de/wiki/Plugins"};
    7575
    76         public static Collection<String> getSites() {
    77                 return Main.pref.getCollection("pluginmanager.sites", Arrays.asList(pluginSites));
    78         }
     76    public static Collection<String> getSites() {
     77        return Main.pref.getCollection("pluginmanager.sites", Arrays.asList(pluginSites));
     78    }
    7979
    80         public static int downloadDescription() {
    81                 int count = 0;
    82                 for (String site : getSites()) {
    83                         try {
    84                                 BufferedReader r = new BufferedReader(new InputStreamReader(new URL(site).openStream()));
    85                                 CharSequence txt;
    86                                 if (site.toLowerCase().endsWith(".xml"))
    87                                         txt = readXml(r);
    88                                 else
    89                                         txt = readWiki(r);
    90                                 r.close();
    91                                 new File(Main.pref.getPreferencesDir()+"plugins").mkdir();
    92                                 FileWriter out = new FileWriter(new File(Main.pref
    93                                         .getPluginsDirFile(), count + "-site-"
    94                                         + site.replaceAll("[/:\\\\ <>|]", "_") + ".xml"));
    95                                 out.append(txt);
    96                                 out.close();
    97                                 count++;
    98                         } catch (IOException x) {
    99                         }
    100                 }
    101                 return count;
    102         }
     80    public static int downloadDescription() {
     81        int count = 0;
     82        for (String site : getSites()) {
     83            try {
     84                BufferedReader r = new BufferedReader(new InputStreamReader(new URL(site).openStream()));
     85                CharSequence txt;
     86                if (site.toLowerCase().endsWith(".xml"))
     87                    txt = readXml(r);
     88                else
     89                    txt = readWiki(r);
     90                r.close();
     91                new File(Main.pref.getPreferencesDir()+"plugins").mkdir();
     92                FileWriter out = new FileWriter(new File(Main.pref
     93                        .getPluginsDirFile(), count + "-site-"
     94                        + site.replaceAll("[/:\\\\ <>|]", "_") + ".xml"));
     95                out.append(txt);
     96                out.close();
     97                count++;
     98            } catch (IOException x) {
     99            }
     100        }
     101        return count;
     102    }
    103103
    104         private static CharSequence readXml(BufferedReader r) throws IOException {
    105                 StringBuilder b = new StringBuilder();
    106                 for (String line = r.readLine(); line != null; line = r.readLine())
    107                         b.append(line+"\n");
    108                 return b;
    109         }
     104    private static CharSequence readXml(BufferedReader r) throws IOException {
     105        StringBuilder b = new StringBuilder();
     106        for (String line = r.readLine(); line != null; line = r.readLine())
     107            b.append(line+"\n");
     108        return b;
     109    }
    110110
    111         private static CharSequence readWiki(BufferedReader r) throws IOException {
    112                 StringBuilder b = new StringBuilder("<plugins>\n");
    113                 for (String line = r.readLine(); line != null; line = r.readLine()) {
    114                         Matcher m = wiki.matcher(line);
    115                         if (!m.matches())
    116                                 continue;
    117                         b.append("  <plugin>\n");
    118                         b.append("    <name>"+escape(m.group(2))+"</name>\n");
    119                         b.append("    <resource>"+escape(m.group(1))+"</resource>\n");
    120                         b.append("    <author>"+escape(m.group(3))+"</author>\n");
    121                         b.append("    <description>"+escape(m.group(4))+"</description>\n");
    122                         b.append("    <version>"+escape(m.group(5))+"</version>\n");
    123                         b.append("  </plugin>\n");
    124                 }
    125                 b.append("</plugins>\n");
    126                 return b;
    127         }
     111    private static CharSequence readWiki(BufferedReader r) throws IOException {
     112        StringBuilder b = new StringBuilder("<plugins>\n");
     113        for (String line = r.readLine(); line != null; line = r.readLine()) {
     114            Matcher m = wiki.matcher(line);
     115            if (!m.matches())
     116                continue;
     117            b.append("  <plugin>\n");
     118            b.append("    <name>"+escape(m.group(2))+"</name>\n");
     119            b.append("    <resource>"+escape(m.group(1))+"</resource>\n");
     120            b.append("    <author>"+escape(m.group(3))+"</author>\n");
     121            b.append("    <description>"+escape(m.group(4))+"</description>\n");
     122            b.append("    <version>"+escape(m.group(5))+"</version>\n");
     123            b.append("  </plugin>\n");
     124        }
     125        b.append("</plugins>\n");
     126        return b;
     127    }
    128128
    129         private static String escape(String s) {
    130                 return s.replaceAll("<", "&lt;").replaceAll(">", "&gt;");
    131         }
     129    private static String escape(String s) {
     130        return s.replaceAll("<", "&lt;").replaceAll(">", "&gt;");
     131    }
    132132
    133         public static boolean downloadPlugin(PluginDescription pd) {
    134                 File file = new File(Main.pref.getPluginsDirFile(), pd.name + ".jar");
    135                 if (!download(pd.resource, file)) {
    136                         JOptionPane.showMessageDialog(Main.parent, tr("Could not download plugin: {0} from {1}", pd.name, pd.resource));
    137                 } else {
    138                         try {
    139                                 PluginInformation.findPlugin(pd.name);
    140                                 return true;
    141                         } catch (Exception e) {
    142                                 e.printStackTrace();
    143                                 JOptionPane.showMessageDialog(Main.parent, tr("The plugin {0} seem to be broken or could not be downloaded automatically.", pd.name));
    144                         }
    145                 }
    146                 if (file.exists())
    147                         file.delete();
    148                 return false;
    149         }
     133    public static boolean downloadPlugin(PluginDescription pd) {
     134        File file = new File(Main.pref.getPluginsDirFile(), pd.name + ".jar");
     135        if (!download(pd.resource, file)) {
     136            JOptionPane.showMessageDialog(Main.parent, tr("Could not download plugin: {0} from {1}", pd.name, pd.resource));
     137        } else {
     138            try {
     139                PluginInformation.findPlugin(pd.name);
     140                return true;
     141            } catch (Exception e) {
     142                e.printStackTrace();
     143                JOptionPane.showMessageDialog(Main.parent, tr("The plugin {0} seem to be broken or could not be downloaded automatically.", pd.name));
     144            }
     145        }
     146        if (file.exists())
     147            file.delete();
     148        return false;
     149    }
    150150
    151         private static boolean download(String url, File file) {
    152                 try {
    153                         InputStream in = new URL(url).openStream();
    154                         OutputStream out = new FileOutputStream(file);
    155                         byte[] buffer = new byte[8192];
    156                         for (int read = in.read(buffer); read != -1; read = in.read(buffer))
    157                                 out.write(buffer, 0, read);
    158                         out.close();
    159                         in.close();
    160                         return true;
    161                 } catch (MalformedURLException e) {
    162                         e.printStackTrace();
    163                 } catch (FileNotFoundException e) {
    164                         e.printStackTrace();
    165                 } catch (IOException e) {
    166                         e.printStackTrace();
    167                 }
    168                 return false;
    169         }
     151    private static boolean download(String url, File file) {
     152        try {
     153            InputStream in = new URL(url).openStream();
     154            OutputStream out = new FileOutputStream(file);
     155            byte[] buffer = new byte[8192];
     156            for (int read = in.read(buffer); read != -1; read = in.read(buffer))
     157                out.write(buffer, 0, read);
     158            out.close();
     159            in.close();
     160            return true;
     161        } catch (MalformedURLException e) {
     162            e.printStackTrace();
     163        } catch (FileNotFoundException e) {
     164            e.printStackTrace();
     165        } catch (IOException e) {
     166            e.printStackTrace();
     167        }
     168        return false;
     169    }
    170170
    171         public static void update(Collection<PluginDescription> update) {
    172                 Main.worker.execute(new UpdateTask(update));
    173         }
    174        
    175         public static boolean moveUpdatedPlugins() {
    176                 File pluginDir = Main.pref.getPluginsDirFile();
    177                 boolean ok = true;             
    178                 if (pluginDir.exists() && pluginDir.isDirectory() && pluginDir.canWrite()) {
    179                         final File[] files = pluginDir.listFiles(new FilenameFilter() {
    180                                 public boolean accept(File dir, String name) {
    181                         return name.endsWith(".new");
     171    public static void update(Collection<PluginDescription> update) {
     172        Main.worker.execute(new UpdateTask(update));
     173    }
     174
     175    public static boolean moveUpdatedPlugins() {
     176        File pluginDir = Main.pref.getPluginsDirFile();
     177        boolean ok = true;
     178        if (pluginDir.exists() && pluginDir.isDirectory() && pluginDir.canWrite()) {
     179            final File[] files = pluginDir.listFiles(new FilenameFilter() {
     180                public boolean accept(File dir, String name) {
     181                    return name.endsWith(".new");
    182182                }});
    183                         for (File updatedPlugin : files) {
    184                                 final String filePath = updatedPlugin.getPath();
    185                                 File plugin = new File(filePath.substring(0, filePath.length() - 4));
    186                                 ok = (plugin.delete() || !plugin.exists()) && updatedPlugin.renameTo(plugin) && ok;
    187                         }
    188                 }
    189                 return ok;
    190         }
     183            for (File updatedPlugin : files) {
     184                final String filePath = updatedPlugin.getPath();
     185                File plugin = new File(filePath.substring(0, filePath.length() - 4));
     186                ok = (plugin.delete() || !plugin.exists()) && updatedPlugin.renameTo(plugin) && ok;
     187            }
     188        }
     189        return ok;
     190    }
    191191}
  • trunk/src/org/openstreetmap/josm/plugins/PluginException.java

    r1116 r1169  
    88 * and there is no particular reason to use this within the plugin itself (although there
    99 * is also no reason against this.. ;)
    10  * 
     10 *
    1111 * @author Immanuel.Scholz
    1212 */
    1313public class PluginException extends RuntimeException {
    14         public final PluginProxy plugin;
    15         public final String name;
     14    public final PluginProxy plugin;
     15    public final String name;
    1616
    17         public PluginException(PluginProxy plugin, String name, Throwable cause) {
    18             super(tr("An error occured in plugin {0}", name), cause);
    19                 this.plugin = plugin;
    20                 this.name = name;
     17    public PluginException(PluginProxy plugin, String name, Throwable cause) {
     18        super(tr("An error occured in plugin {0}", name), cause);
     19        this.plugin = plugin;
     20        this.name = name;
    2121    }
    2222}
  • trunk/src/org/openstreetmap/josm/plugins/PluginInformation.java

    r1073 r1169  
    2929 */
    3030public class PluginInformation {
    31        
    32         public final File file;
    33         public final String name;
    34         public final String mainversion;
    35         public final String className;
    36         public final String description;
    37         public final boolean early;
    38         public final String author;
    39         public final int stage;
    40         public final String version;
    41         public final List<URL> libraries = new LinkedList<URL>();
    42 
    43         public final Map<String, String> attr = new TreeMap<String, String>();
    44 
    45         /**
    46         * Used in the Plugin constructor to make the information of the plugin
    47         * that is currently initializing available.
    48          *
    49          * If you think this is hacky, you are probably right. But it is
    50          * convinient anyway ;-)
    51         */
    52         static PluginInformation currentPluginInitialization = null;
    53 
    54         /**
    55         * @param file the plugin jar file.
    56         */
    57         public PluginInformation(File file) {
    58                 this(file, file.getName().substring(0, file.getName().length()-4), null);
    59         }
    60 
    61         public PluginInformation(File file, String name, InputStream manifestStream) {
    62                 this.name = name;
    63                 this.file = file;
    64                 try {
    65                         Manifest manifest;
    66                         JarInputStream jar = null;
    67                         if (file != null) {
    68                                 jar = new JarInputStream(new FileInputStream(file));
    69                                 manifest = jar.getManifest();
    70                                 if (manifest == null)
    71                                         throw new IOException(file+" contains no manifest.");
    72                         } else {
    73                                 manifest = new Manifest();
    74                         manifest.read(manifestStream);
    75                         }
    76                         if (manifest != null) {
    77                                 Attributes attr = manifest.getMainAttributes();
    78                                 className = attr.getValue("Plugin-Class");
    79                                 description = attr.getValue("Plugin-Description");
    80                                 early = Boolean.parseBoolean(attr.getValue("Plugin-Early"));
    81                                 String stageStr = attr.getValue("Plugin-Stage");
    82                                 stage = stageStr == null ? 50 : Integer.parseInt(stageStr);
    83                                 version = attr.getValue("Plugin-Version");
    84                                 mainversion = attr.getValue("Plugin-Mainversion");
    85                                 author = attr.getValue("Author");
    86 
    87                                 String classPath = attr.getValue(Attributes.Name.CLASS_PATH);
    88                                 if (classPath != null) {
    89                                         for (String entry : classPath.split(" ")) {
    90                                                 File entryFile;
    91                                                 if (new File(entry).isAbsolute()) {
    92                                                         entryFile = new File(entry);
    93                                                 } else {
    94                                                         entryFile = new File(file.getParent(), entry);
    95                                                 }
    96 
    97                                                 libraries.add(fileToURL(entryFile));
    98                                         }
    99                                 }
    100                                 for (Object o : attr.keySet())
    101                                         this.attr.put(o.toString(), attr.getValue(o.toString()));
    102                         } else {
    103                                 // resource-only plugin
    104                                 className = null;
    105                                 mainversion = null;
    106                                 description = tr("unknown");
    107                                 early = false;
    108                                 stage = 50;
    109                                 version = null;
    110                                 author = null;
    111                         }
    112                         if (file != null)
    113                                 libraries.add(0, fileToURL(file));
    114 
    115                         if (jar != null)
    116                                 jar.close();
    117                 } catch (IOException e) {
    118                         throw new PluginException(null, name, e);
    119                 }
    120         }
    121 
    122         /**
    123         * Load and instantiate the plugin
    124         */
    125         public PluginProxy load(Class<?> klass) {
    126                 try {
    127                         currentPluginInitialization = this;
    128                         return new PluginProxy(klass.newInstance(), this);
    129                 } catch (Exception e) {
    130                         throw new PluginException(null, name, e);
    131                 }
    132         }
    133 
    134         /**
    135         * Load the class of the plugin
    136         */
    137         public Class<?> loadClass(ClassLoader classLoader) {
    138                 if (className == null)
    139                         return null;
    140                 try {
    141                         Class<?> realClass = Class.forName(className, true, classLoader);
    142                         return realClass;
    143                 } catch (Exception e) {
    144                         throw new PluginException(null, name, e);
    145                 }
    146         }
    147 
    148         public static URL fileToURL(File f) {
    149                 try {
    150                         return f.toURI().toURL();
    151                 } catch (MalformedURLException ex) {
    152                         return null;
    153                 }
    154         }
    155 
    156         /**
    157         * Try to find a plugin after some criterias. Extract the plugin-information
    158         * from the plugin and return it. The plugin is searched in the following way:
    159          *
    160         *<li>first look after an MANIFEST.MF in the package org.openstreetmap.josm.plugins.<plugin name>
    161         *    (After removing all fancy characters from the plugin name).
    162         *    If found, the plugin is loaded using the bootstrap classloader.
    163         *<li>If not found, look for a jar file in the user specific plugin directory
    164         *    (~/.josm/plugins/<plugin name>.jar)
    165         *<li>If not found and the environment variable JOSM_RESSOURCES + "/plugins/" exist, look there.
    166         *<li>Try for the java property josm.ressources + "/plugins/" (set via java -Djosm.plugins.path=...)
    167          *<li>If the environment variable ALLUSERSPROFILE and APPDATA exist, look in
    168          *    ALLUSERSPROFILE/<the last stuff from APPDATA>/JOSM/plugins.
    169          *    (*sic* There is no easy way under Windows to get the All User's application
    170         *    directory)
    171          *<li>Finally, look in some typical unix paths:<ul>
    172         *    <li>/usr/local/share/josm/plugins/
    173         *    <li>/usr/local/lib/josm/plugins/
    174         *    <li>/usr/share/josm/plugins/
    175         *    <li>/usr/lib/josm/plugins/
    176          *
    177         * If a plugin class or jar file is found earlier in the list but seem not to
    178         * be working, an PluginException is thrown rather than continuing the search.
    179         * This is so JOSM can detect broken user-provided plugins and do not go silently
    180          * ignore them.
    181          *
    182         * The plugin is not initialized. If the plugin is a .jar file, it is not loaded
    183          * (only the manifest is extracted). In the classloader-case, the class is
    184         * bootstraped (e.g. static {} - declarations will run. However, nothing else is done.
    185         *
    186         * @param pluginName The name of the plugin (in all lowercase). E.g. "lang-de"
    187         * @return Information about the plugin or <code>null</code>, if the plugin
    188          *             was nowhere to be found.
    189         * @throws PluginException In case of broken plugins.
    190         */
    191         public static PluginInformation findPlugin(String pluginName) throws PluginException {
    192         String name = pluginName;
    193         name = name.replaceAll("[-. ]", "");
    194         InputStream manifestStream = PluginInformation.class.getResourceAsStream("/org/openstreetmap/josm/plugins/"+name+"/MANIFEST.MF");
    195         if (manifestStream != null)
    196                 return new PluginInformation(null, pluginName, manifestStream);
     31
     32    public final File file;
     33    public final String name;
     34    public final String mainversion;
     35    public final String className;
     36    public final String description;
     37    public final boolean early;
     38    public final String author;
     39    public final int stage;
     40    public final String version;
     41    public final List<URL> libraries = new LinkedList<URL>();
     42
     43    public final Map<String, String> attr = new TreeMap<String, String>();
     44
     45    /**
     46    * Used in the Plugin constructor to make the information of the plugin
     47    * that is currently initializing available.
     48     *
     49     * If you think this is hacky, you are probably right. But it is
     50     * convinient anyway ;-)
     51    */
     52    static PluginInformation currentPluginInitialization = null;
     53
     54    /**
     55    * @param file the plugin jar file.
     56    */
     57    public PluginInformation(File file) {
     58        this(file, file.getName().substring(0, file.getName().length()-4), null);
     59    }
     60
     61    public PluginInformation(File file, String name, InputStream manifestStream) {
     62        this.name = name;
     63        this.file = file;
     64        try {
     65            Manifest manifest;
     66            JarInputStream jar = null;
     67            if (file != null) {
     68                jar = new JarInputStream(new FileInputStream(file));
     69                manifest = jar.getManifest();
     70                if (manifest == null)
     71                    throw new IOException(file+" contains no manifest.");
     72            } else {
     73                manifest = new Manifest();
     74                manifest.read(manifestStream);
     75            }
     76            if (manifest != null) {
     77                Attributes attr = manifest.getMainAttributes();
     78                className = attr.getValue("Plugin-Class");
     79                description = attr.getValue("Plugin-Description");
     80                early = Boolean.parseBoolean(attr.getValue("Plugin-Early"));
     81                String stageStr = attr.getValue("Plugin-Stage");
     82                stage = stageStr == null ? 50 : Integer.parseInt(stageStr);
     83                version = attr.getValue("Plugin-Version");
     84                mainversion = attr.getValue("Plugin-Mainversion");
     85                author = attr.getValue("Author");
     86
     87                String classPath = attr.getValue(Attributes.Name.CLASS_PATH);
     88                if (classPath != null) {
     89                    for (String entry : classPath.split(" ")) {
     90                        File entryFile;
     91                        if (new File(entry).isAbsolute()) {
     92                            entryFile = new File(entry);
     93                        } else {
     94                            entryFile = new File(file.getParent(), entry);
     95                        }
     96
     97                        libraries.add(fileToURL(entryFile));
     98                    }
     99                }
     100                for (Object o : attr.keySet())
     101                    this.attr.put(o.toString(), attr.getValue(o.toString()));
     102            } else {
     103                // resource-only plugin
     104                className = null;
     105                mainversion = null;
     106                description = tr("unknown");
     107                early = false;
     108                stage = 50;
     109                version = null;
     110                author = null;
     111            }
     112            if (file != null)
     113                libraries.add(0, fileToURL(file));
     114
     115            if (jar != null)
     116                jar.close();
     117        } catch (IOException e) {
     118            throw new PluginException(null, name, e);
     119        }
     120    }
     121
     122    /**
     123    * Load and instantiate the plugin
     124    */
     125    public PluginProxy load(Class<?> klass) {
     126        try {
     127            currentPluginInitialization = this;
     128            return new PluginProxy(klass.newInstance(), this);
     129        } catch (Exception e) {
     130            throw new PluginException(null, name, e);
     131        }
     132    }
     133
     134    /**
     135    * Load the class of the plugin
     136    */
     137    public Class<?> loadClass(ClassLoader classLoader) {
     138        if (className == null)
     139            return null;
     140        try {
     141            Class<?> realClass = Class.forName(className, true, classLoader);
     142            return realClass;
     143        } catch (Exception e) {
     144            throw new PluginException(null, name, e);
     145        }
     146    }
     147
     148    public static URL fileToURL(File f) {
     149        try {
     150            return f.toURI().toURL();
     151        } catch (MalformedURLException ex) {
     152            return null;
     153        }
     154    }
     155
     156    /**
     157    * Try to find a plugin after some criterias. Extract the plugin-information
     158    * from the plugin and return it. The plugin is searched in the following way:
     159     *
     160    *<li>first look after an MANIFEST.MF in the package org.openstreetmap.josm.plugins.<plugin name>
     161    *    (After removing all fancy characters from the plugin name).
     162    *    If found, the plugin is loaded using the bootstrap classloader.
     163    *<li>If not found, look for a jar file in the user specific plugin directory
     164    *    (~/.josm/plugins/<plugin name>.jar)
     165    *<li>If not found and the environment variable JOSM_RESSOURCES + "/plugins/" exist, look there.
     166    *<li>Try for the java property josm.ressources + "/plugins/" (set via java -Djosm.plugins.path=...)
     167     *<li>If the environment variable ALLUSERSPROFILE and APPDATA exist, look in
     168     *    ALLUSERSPROFILE/<the last stuff from APPDATA>/JOSM/plugins.
     169     *    (*sic* There is no easy way under Windows to get the All User's application
     170    *    directory)
     171     *<li>Finally, look in some typical unix paths:<ul>
     172    *    <li>/usr/local/share/josm/plugins/
     173    *    <li>/usr/local/lib/josm/plugins/
     174    *    <li>/usr/share/josm/plugins/
     175    *    <li>/usr/lib/josm/plugins/
     176     *
     177    * If a plugin class or jar file is found earlier in the list but seem not to
     178    * be working, an PluginException is thrown rather than continuing the search.
     179    * This is so JOSM can detect broken user-provided plugins and do not go silently
     180     * ignore them.
     181     *
     182    * The plugin is not initialized. If the plugin is a .jar file, it is not loaded
     183     * (only the manifest is extracted). In the classloader-case, the class is
     184    * bootstraped (e.g. static {} - declarations will run. However, nothing else is done.
     185    *
     186    * @param pluginName The name of the plugin (in all lowercase). E.g. "lang-de"
     187    * @return Information about the plugin or <code>null</code>, if the plugin
     188     *         was nowhere to be found.
     189    * @throws PluginException In case of broken plugins.
     190    */
     191    public static PluginInformation findPlugin(String pluginName) throws PluginException {
     192        String name = pluginName;
     193        name = name.replaceAll("[-. ]", "");
     194        InputStream manifestStream = PluginInformation.class.getResourceAsStream("/org/openstreetmap/josm/plugins/"+name+"/MANIFEST.MF");
     195        if (manifestStream != null)
     196            return new PluginInformation(null, pluginName, manifestStream);
    197197
    198198        Collection<String> locations = getPluginLocations();
    199199
    200         for (String s : locations) {
    201                 File pluginFile = new File(s, pluginName + ".jar");
    202                 if (pluginFile.exists()) {
    203                                 PluginInformation info = new PluginInformation(pluginFile);
    204                                 return info;
    205                 }
    206         }
    207         return null;
    208         }
    209 
    210         public static Collection<String> getPluginLocations() {
    211             Collection<String> locations = Main.pref.getAllPossiblePreferenceDirs();
    212             Collection<String> all = new ArrayList<String>(locations.size());
    213             for (String s : locations)
    214                 all.add(s+"plugins");
    215             return all;
    216     }
    217        
    218        
    219         /**
    220         * Return information about a loaded plugin.
    221          *
    222         * Note that if you call this in your plugins bootstrap, you may get <code>null</code> if
    223         * the plugin requested is not loaded yet.
    224          *
    225         * @return The PluginInformation to a specific plugin, but only if the plugin is loaded.
    226         * If it is not loaded, <code>null</code> is returned.
    227         */
    228         public static PluginInformation getLoaded(String pluginName) {
    229                 for (PluginProxy p : Main.plugins)
    230                         if (p.info.name.equals(pluginName))
    231                                 return p.info;
    232                 return null;
    233         }
     200        for (String s : locations) {
     201            File pluginFile = new File(s, pluginName + ".jar");
     202            if (pluginFile.exists()) {
     203                PluginInformation info = new PluginInformation(pluginFile);
     204                return info;
     205            }
     206        }
     207        return null;
     208    }
     209
     210    public static Collection<String> getPluginLocations() {
     211        Collection<String> locations = Main.pref.getAllPossiblePreferenceDirs();
     212        Collection<String> all = new ArrayList<String>(locations.size());
     213        for (String s : locations)
     214            all.add(s+"plugins");
     215        return all;
     216    }
     217
     218
     219    /**
     220    * Return information about a loaded plugin.
     221     *
     222    * Note that if you call this in your plugins bootstrap, you may get <code>null</code> if
     223    * the plugin requested is not loaded yet.
     224     *
     225    * @return The PluginInformation to a specific plugin, but only if the plugin is loaded.
     226    * If it is not loaded, <code>null</code> is returned.
     227    */
     228    public static PluginInformation getLoaded(String pluginName) {
     229        for (PluginProxy p : Main.plugins)
     230            if (p.info.name.equals(pluginName))
     231                return p.info;
     232        return null;
     233    }
    234234}
    235235
  • trunk/src/org/openstreetmap/josm/plugins/PluginProxy.java

    r627 r1169  
    1818public class PluginProxy extends Plugin {
    1919
    20         public final Object plugin;
    21         public final PluginInformation info;
     20    public final Object plugin;
     21    public final PluginInformation info;
    2222
    23         public PluginProxy(Object plugin, PluginInformation info) {
    24                 this.plugin = plugin;
    25                 this.info = info;
     23    public PluginProxy(Object plugin, PluginInformation info) {
     24        this.plugin = plugin;
     25        this.info = info;
    2626    }
    2727
    28         @Override public void mapFrameInitialized(MapFrame oldFrame, MapFrame newFrame) {
    29                 try {
    30                 plugin.getClass().getMethod("mapFrameInitialized", MapFrame.class, MapFrame.class).invoke(plugin, oldFrame, newFrame);
     28    @Override public void mapFrameInitialized(MapFrame oldFrame, MapFrame newFrame) {
     29        try {
     30            plugin.getClass().getMethod("mapFrameInitialized", MapFrame.class, MapFrame.class).invoke(plugin, oldFrame, newFrame);
    3131        } catch (NoSuchMethodException e) {
    3232        } catch (Exception e) {
    33                 throw new PluginException(this, info.name, e);
     33            throw new PluginException(this, info.name, e);
    3434        }
    3535    }
    3636
    37         @Override public PreferenceSetting getPreferenceSetting() {
    38                 try {
    39                         return (PreferenceSetting)plugin.getClass().getMethod("getPreferenceSetting").invoke(plugin);
    40                 } catch (NoSuchMethodException e) {
    41                         return null;
    42                 } catch (Exception e) {
    43                         throw new PluginException(this, info.name, e);
    44                 }
     37    @Override public PreferenceSetting getPreferenceSetting() {
     38        try {
     39            return (PreferenceSetting)plugin.getClass().getMethod("getPreferenceSetting").invoke(plugin);
     40        } catch (NoSuchMethodException e) {
     41            return null;
     42        } catch (Exception e) {
     43            throw new PluginException(this, info.name, e);
     44        }
    4545    }
    46        
    47         @Override public void addDownloadSelection(List<DownloadSelection> list) {
    48                 try {
    49                         plugin.getClass().getMethod("addDownloadSelection", List.class).invoke(plugin, list);
    50                 } catch (NoSuchMethodException e) {
    51                         // ignore
    52                 } catch (Exception e) {
    53                         throw new PluginException(this, info.name, e);
    54                 }
    55         }
     46
     47    @Override public void addDownloadSelection(List<DownloadSelection> list) {
     48        try {
     49            plugin.getClass().getMethod("addDownloadSelection", List.class).invoke(plugin, list);
     50        } catch (NoSuchMethodException e) {
     51            // ignore
     52        } catch (Exception e) {
     53            throw new PluginException(this, info.name, e);
     54        }
     55    }
    5656}
  • trunk/src/org/openstreetmap/josm/tools/AudioPlayer.java

    r891 r1169  
    1818/**
    1919 * Creates and controls a separate audio player thread.
    20  * 
     20 *
    2121 * @author David Earl <david@frankieandshadow.com>
    2222 *
     
    2424public class AudioPlayer extends Thread {
    2525
    26         private static AudioPlayer audioPlayer = null;
    27 
    28         private enum State { INITIALIZING, NOTPLAYING, PLAYING, PAUSED, INTERRUPTED }
    29         private State state;
     26    private static AudioPlayer audioPlayer = null;
     27
     28    private enum State { INITIALIZING, NOTPLAYING, PLAYING, PAUSED, INTERRUPTED }
     29    private State state;
    3030    private enum Command { PLAY, PAUSE }
    3131    private enum Result { WAITING, OK, FAILED }
     
    3333    private double leadIn; // seconds
    3434    private double calibration; // ratio of purported duration of samples to true duration
    35         private double position; // seconds
    36         private double bytesPerSecond;
    37         private static long chunk = 4000; /* bytes */
    38         private double speed = 1.0;
    39 
    40         /**
    41          * Passes information from the control thread to the playing thread
    42         */
    43         private class Execute {
    44                 private Command command;
    45                 private Result result;
    46                 private Exception exception;
    47                 private URL url;
    48                 private double offset; // seconds
    49                 private double speed; // ratio
    50                
    51                 /*
    52                  * Called to execute the commands in the other thread
    53                 */
    54                 protected void play(URL url, double offset, double speed) throws Exception {
    55                         this.url = url;
    56                         this.offset = offset;
    57                         this.speed = speed;
    58                         command = Command.PLAY;
    59                         result = Result.WAITING;
    60                         send();
    61                 }
    62                 protected void pause() throws Exception {
    63                         command = Command.PAUSE;
    64                         send();
    65                 }
    66                 private void send() throws Exception {
    67                         result = Result.WAITING;
    68                         interrupt();
    69                         while (result == Result.WAITING) { sleep(10); /* yield(); */ }
    70                         if (result == Result.FAILED) { throw exception; }
    71                 }
    72                 private void possiblyInterrupt() throws InterruptedException {
    73                         if (interrupted() || result == Result.WAITING)
    74                                 throw new InterruptedException();
    75                 }
    76                 protected void failed (Exception e) {
    77                         exception = e;
    78                         result = Result.FAILED;
    79                         state = State.NOTPLAYING;
    80                 }
    81                 protected void ok (State newState) {
    82                         result = Result.OK;
    83                         state = newState;
    84                 }
    85                 protected double offset() {
    86                         return offset;
    87                 }
    88                 protected double speed() {
    89                         return speed;
    90                 }
    91                 protected URL url() {
    92                         return url;
    93                 }
    94                 protected Command command() {
    95                         return command;
    96                 }
    97         }
    98        
    99         private Execute command;
    100 
    101         /**
    102          * Plays a WAV audio file from the beginning. See also the variant which doesn't
    103         * start at the beginning of the stream
    104         * @param url The resource to play, which must be a WAV file or stream
    105         * @throws audio fault exception, e.g. can't open stream,  unhandleable audio format
    106         */
    107         public static void play(URL url) throws Exception {
    108                 AudioPlayer.get().command.play(url, 0.0, 1.0);
    109         }
    110        
    111         /**
    112         * Plays a WAV audio file from a specified position.
    113         * @param url The resource to play, which must be a WAV file or stream
    114         * @param seconds The number of seconds into the audio to start playing
    115         * @throws audio fault exception, e.g. can't open stream,  unhandleable audio format
    116         */
    117         public static void play(URL url, double seconds) throws Exception {
    118                 AudioPlayer.get().command.play(url, seconds, 1.0);
    119         }
    120        
    121         /**
    122         * Plays a WAV audio file from a specified position at variable speed.
    123         * @param url The resource to play, which must be a WAV file or stream
    124         * @param seconds The number of seconds into the audio to start playing
    125         * @param speed Rate at which audio playes (1.0 = real time, > 1 is faster)
    126         * @throws audio fault exception, e.g. can't open stream,  unhandleable audio format
    127         */
    128         public static void play(URL url, double seconds, double speed) throws Exception {
    129                 AudioPlayer.get().command.play(url, seconds, speed);
    130         }
    131        
    132         /**
    133         * Pauses the currently playing audio stream. Does nothing if nothing playing.
    134         * @throws audio fault exception, e.g. can't open stream,  unhandleable audio format
    135         */
    136         public static void pause() throws Exception {
    137                 AudioPlayer.get().command.pause();
    138         }
    139        
    140         /**
    141         * To get the Url of the playing or recently played audio.
    142         * @return url - could be null
    143         */
    144         public static URL url() {
    145                 return AudioPlayer.get().playingUrl;
    146         }
    147        
    148         /**
    149         * Whether or not we are paused.
    150         * @return boolean whether or not paused
    151         */
    152         public static boolean paused() {
    153                 return AudioPlayer.get().state == State.PAUSED;
    154         }
    155 
    156         /**
    157         * Whether or not we are playing.
    158         * @return boolean whether or not playing
    159         */
    160         public static boolean playing() {
    161                 return AudioPlayer.get().state == State.PLAYING;
    162         }
    163 
    164         /**
    165         * How far we are through playing, in seconds.
    166         * @return double seconds
    167         */
    168         public static double position() {
    169                 return AudioPlayer.get().position;
    170         }
    171        
    172         /**
    173         * Speed at which we will play.
    174         * @return double, speed multiplier
    175         */
    176         public static double speed() {
    177                 return AudioPlayer.get().speed;
    178         }
    179 
    180         /**
    181          *  gets the singleton object, and if this is the first time, creates it along with
    182         *  the thread to support audio
    183         */
    184         private static AudioPlayer get() {
    185                 if (audioPlayer != null)
    186                         return audioPlayer;
    187                 try {
    188                         audioPlayer = new AudioPlayer();
    189                         return audioPlayer;
    190                 } catch (Exception ex) {
    191                         return null;
    192                 }
    193         }
    194 
    195         private AudioPlayer() {
    196                 state = State.INITIALIZING;
    197                 command = new Execute();
    198                 playingUrl = null;
    199                 try {
    200                         leadIn = Double.parseDouble(Main.pref.get("audio.leadin", "1.0" /* default, seconds */));
    201                 } catch (NumberFormatException e) {
    202                         leadIn = 1.0; // failed to parse
    203                 }
    204                 try {
    205                         calibration = Double.parseDouble(Main.pref.get("audio.calibration", "1.0" /* default, ratio */));
    206                 } catch (NumberFormatException e) {
    207                         calibration = 1.0; // failed to parse
    208                 }
    209                 start();
    210                 while (state == State.INITIALIZING) { yield(); }
    211         }
    212 
    213         /**
    214         * Starts the thread to actually play the audio, per Thread interface
    215         * Not to be used as public, though Thread interface doesn't allow it to be made private
    216         */
    217         @Override public void run() {
    218                 /* code running in separate thread */
    219 
    220                 playingUrl = null;
    221                 AudioInputStream audioInputStream = null;
    222                 SourceDataLine audioOutputLine = null;
    223                 AudioFormat     audioFormat = null;
    224                 byte[] abData = new byte[(int)chunk];
    225                
    226                 for (;;) {
    227                         try {
    228                                 switch (state) {
    229                                 case INITIALIZING:
    230                                         // we're ready to take interrupts
    231                                         state = State.NOTPLAYING;
    232                                         break;
    233                                 case NOTPLAYING:
    234                                 case PAUSED:
    235                                         sleep(200);
    236                                         break;
    237                                 case PLAYING:
    238                                         command.possiblyInterrupt();
    239                                         for(;;) {
    240                                                 int nBytesRead = 0;
    241                                                 nBytesRead = audioInputStream.read(abData, 0, abData.length);
    242                                                 position += nBytesRead / bytesPerSecond;
    243                                                 command.possiblyInterrupt();
    244                                                 if (nBytesRead < 0) { break; }
    245                                                 audioOutputLine.write(abData, 0, nBytesRead); // => int nBytesWritten
    246                                                 command.possiblyInterrupt();
    247                                         }
    248                                         // end of audio, clean up
    249                                         audioOutputLine.drain();
    250                                         audioOutputLine.close();
    251                                         audioOutputLine = null;
    252                                         audioInputStream.close();
    253                                         audioInputStream = null;
    254                                         playingUrl = null;
    255                                         state = State.NOTPLAYING;
    256                                         command.possiblyInterrupt();
    257                                         break;
    258                                 }
    259                         } catch (InterruptedException e) {
    260                                 interrupted(); // just in case we get an interrupt
    261                                 State stateChange = state;
    262                                 state = State.INTERRUPTED;
    263                                 try {
    264                                         switch (command.command()) {
    265                                         case PLAY:     
    266                                                 double offset = command.offset();
    267                                                 speed = command.speed();
    268                                                 if (playingUrl != command.url() ||
    269                                                         stateChange != State.PAUSED ||
    270                                                         offset != 0.0)
    271                                                 {
    272                                                         if (audioInputStream != null) {
    273                                                                 audioInputStream.close();
    274                                                                 audioInputStream = null;
    275                                                         }
    276                                                         playingUrl = command.url();
    277                                                         audioInputStream = AudioSystem.getAudioInputStream(playingUrl);
    278                                                         audioFormat = audioInputStream.getFormat();
    279                                                         long nBytesRead = 0;
    280                                                         position = 0.0;
    281                                                         offset -= leadIn;
    282                                                         double calibratedOffset = offset * calibration;
    283                                                         bytesPerSecond = audioFormat.getFrameRate() /* frames per second */
    284                                                                 * audioFormat.getFrameSize() /* bytes per frame */;
    285                                                         if (speed * bytesPerSecond > 256000.0)
    286                                                                 speed = 256000 / bytesPerSecond;
    287                                                         if (calibratedOffset > 0.0) {
    288                                                                 long bytesToSkip = (long)(
    289                                                                                 calibratedOffset /* seconds (double) */ * bytesPerSecond);
    290                                                                 /* skip doesn't seem to want to skip big chunks, so
    291                                                                  * reduce it to smaller ones
    292                                                                 */
    293                                                                 // audioInputStream.skip(bytesToSkip);
    294                                                                 while (bytesToSkip > chunk) {
    295                                                                         nBytesRead = audioInputStream.skip(chunk);
    296                                                                         if (nBytesRead <= 0)
    297                                                                                 throw new IOException(tr("This is after the end of the recording"));
    298                                                                         bytesToSkip -= nBytesRead;
    299                                                                 }
    300                                                                 if (bytesToSkip > 0)
    301                                                                         audioInputStream.skip(bytesToSkip);
    302                                                                 position = offset;
    303                                                         }
    304                                                         if (audioOutputLine != null)
    305                                                                 audioOutputLine.close();
    306                                                         audioFormat = new AudioFormat(audioFormat.getEncoding(),
    307                                                                                 audioFormat.getSampleRate() * (float) (speed * calibration),
    308                                                                                 audioFormat.getSampleSizeInBits(),
    309                                                                                 audioFormat.getChannels(),
    310                                                                                 audioFormat.getFrameSize(),
    311                                                                                 audioFormat.getFrameRate() * (float) (speed * calibration),
    312                                                                                 audioFormat.isBigEndian());
    313                                                         DataLine.Info info = new DataLine.Info(SourceDataLine.class, audioFormat);
    314                                                         audioOutputLine = (SourceDataLine) AudioSystem.getLine(info);
    315                                                         audioOutputLine.open(audioFormat);
    316                                                         audioOutputLine.start();
    317                                                 }
    318                                                 stateChange = State.PLAYING;
    319                                                 break;
    320                                         case PAUSE:
    321                                                 stateChange = State.PAUSED;
    322                                                 break;
    323                                         }
    324                                         command.ok(stateChange);
    325                                 } catch (Exception startPlayingException) {
    326                                         command.failed(startPlayingException); // sets state
    327                                 }
    328                         } catch (Exception e) {
    329                                 state = State.NOTPLAYING;
    330                         }
    331                 }
    332         }
    333 
    334         public static void audioMalfunction(Exception ex) {
    335                 JOptionPane.showMessageDialog(Main.parent,
    336                                 "<html><p>" + tr(ex.getMessage()) + "</p></html>",
    337                                 tr("Error playing sound"), JOptionPane.ERROR_MESSAGE);
    338         }
     35    private double position; // seconds
     36    private double bytesPerSecond;
     37    private static long chunk = 4000; /* bytes */
     38    private double speed = 1.0;
     39
     40    /**
     41     * Passes information from the control thread to the playing thread
     42    */
     43    private class Execute {
     44        private Command command;
     45        private Result result;
     46        private Exception exception;
     47        private URL url;
     48        private double offset; // seconds
     49        private double speed; // ratio
     50
     51        /*
     52         * Called to execute the commands in the other thread
     53        */
     54        protected void play(URL url, double offset, double speed) throws Exception {
     55            this.url = url;
     56            this.offset = offset;
     57            this.speed = speed;
     58            command = Command.PLAY;
     59            result = Result.WAITING;
     60            send();
     61        }
     62        protected void pause() throws Exception {
     63            command = Command.PAUSE;
     64            send();
     65        }
     66        private void send() throws Exception {
     67            result = Result.WAITING;
     68            interrupt();
     69            while (result == Result.WAITING) { sleep(10); /* yield(); */ }
     70            if (result == Result.FAILED) { throw exception; }
     71        }
     72        private void possiblyInterrupt() throws InterruptedException {
     73            if (interrupted() || result == Result.WAITING)
     74                throw new InterruptedException();
     75        }
     76        protected void failed (Exception e) {
     77            exception = e;
     78            result = Result.FAILED;
     79            state = State.NOTPLAYING;
     80        }
     81        protected void ok (State newState) {
     82            result = Result.OK;
     83            state = newState;
     84        }
     85        protected double offset() {
     86            return offset;
     87        }
     88        protected double speed() {
     89            return speed;
     90        }
     91        protected URL url() {
     92            return url;
     93        }
     94        protected Command command() {
     95            return command;
     96        }
     97    }
     98
     99    private Execute command;
     100
     101    /**
     102     * Plays a WAV audio file from the beginning. See also the variant which doesn't
     103    * start at the beginning of the stream
     104    * @param url The resource to play, which must be a WAV file or stream
     105    * @throws audio fault exception, e.g. can't open stream,  unhandleable audio format
     106    */
     107    public static void play(URL url) throws Exception {
     108        AudioPlayer.get().command.play(url, 0.0, 1.0);
     109    }
     110
     111    /**
     112    * Plays a WAV audio file from a specified position.
     113    * @param url The resource to play, which must be a WAV file or stream
     114    * @param seconds The number of seconds into the audio to start playing
     115    * @throws audio fault exception, e.g. can't open stream,  unhandleable audio format
     116    */
     117    public static void play(URL url, double seconds) throws Exception {
     118        AudioPlayer.get().command.play(url, seconds, 1.0);
     119    }
     120
     121    /**
     122    * Plays a WAV audio file from a specified position at variable speed.
     123    * @param url The resource to play, which must be a WAV file or stream
     124    * @param seconds The number of seconds into the audio to start playing
     125    * @param speed Rate at which audio playes (1.0 = real time, > 1 is faster)
     126    * @throws audio fault exception, e.g. can't open stream,  unhandleable audio format
     127    */
     128    public static void play(URL url, double seconds, double speed) throws Exception {
     129        AudioPlayer.get().command.play(url, seconds, speed);
     130    }
     131
     132    /**
     133    * Pauses the currently playing audio stream. Does nothing if nothing playing.
     134    * @throws audio fault exception, e.g. can't open stream,  unhandleable audio format
     135    */
     136    public static void pause() throws Exception {
     137        AudioPlayer.get().command.pause();
     138    }
     139
     140    /**
     141    * To get the Url of the playing or recently played audio.
     142    * @return url - could be null
     143    */
     144    public static URL url() {
     145        return AudioPlayer.get().playingUrl;
     146    }
     147
     148    /**
     149    * Whether or not we are paused.
     150    * @return boolean whether or not paused
     151    */
     152    public static boolean paused() {
     153        return AudioPlayer.get().state == State.PAUSED;
     154    }
     155
     156    /**
     157    * Whether or not we are playing.
     158    * @return boolean whether or not playing
     159    */
     160    public static boolean playing() {
     161        return AudioPlayer.get().state == State.PLAYING;
     162    }
     163
     164    /**
     165    * How far we are through playing, in seconds.
     166    * @return double seconds
     167    */
     168    public static double position() {
     169        return AudioPlayer.get().position;
     170    }
     171
     172    /**
     173    * Speed at which we will play.
     174    * @return double, speed multiplier
     175    */
     176    public static double speed() {
     177        return AudioPlayer.get().speed;
     178    }
     179
     180    /**
     181     *  gets the singleton object, and if this is the first time, creates it along with
     182    *  the thread to support audio
     183    */
     184    private static AudioPlayer get() {
     185        if (audioPlayer != null)
     186            return audioPlayer;
     187        try {
     188            audioPlayer = new AudioPlayer();
     189            return audioPlayer;
     190        } catch (Exception ex) {
     191            return null;
     192        }
     193    }
     194
     195    private AudioPlayer() {
     196        state = State.INITIALIZING;
     197        command = new Execute();
     198        playingUrl = null;
     199        try {
     200            leadIn = Double.parseDouble(Main.pref.get("audio.leadin", "1.0" /* default, seconds */));
     201        } catch (NumberFormatException e) {
     202            leadIn = 1.0; // failed to parse
     203        }
     204        try {
     205            calibration = Double.parseDouble(Main.pref.get("audio.calibration", "1.0" /* default, ratio */));
     206        } catch (NumberFormatException e) {
     207            calibration = 1.0; // failed to parse
     208        }
     209        start();
     210        while (state == State.INITIALIZING) { yield(); }
     211    }
     212
     213    /**
     214    * Starts the thread to actually play the audio, per Thread interface
     215    * Not to be used as public, though Thread interface doesn't allow it to be made private
     216    */
     217    @Override public void run() {
     218        /* code running in separate thread */
     219
     220        playingUrl = null;
     221        AudioInputStream audioInputStream = null;
     222        SourceDataLine audioOutputLine = null;
     223        AudioFormat audioFormat = null;
     224        byte[] abData = new byte[(int)chunk];
     225
     226        for (;;) {
     227            try {
     228                switch (state) {
     229                case INITIALIZING:
     230                    // we're ready to take interrupts
     231                    state = State.NOTPLAYING;
     232                    break;
     233                case NOTPLAYING:
     234                case PAUSED:
     235                    sleep(200);
     236                    break;
     237                case PLAYING:
     238                    command.possiblyInterrupt();
     239                    for(;;) {
     240                        int nBytesRead = 0;
     241                        nBytesRead = audioInputStream.read(abData, 0, abData.length);
     242                        position += nBytesRead / bytesPerSecond;
     243                        command.possiblyInterrupt();
     244                        if (nBytesRead < 0) { break; }
     245                        audioOutputLine.write(abData, 0, nBytesRead); // => int nBytesWritten
     246                        command.possiblyInterrupt();
     247                    }
     248                    // end of audio, clean up
     249                    audioOutputLine.drain();
     250                    audioOutputLine.close();
     251                    audioOutputLine = null;
     252                    audioInputStream.close();
     253                    audioInputStream = null;
     254                    playingUrl = null;
     255                    state = State.NOTPLAYING;
     256                    command.possiblyInterrupt();
     257                    break;
     258                }
     259            } catch (InterruptedException e) {
     260                interrupted(); // just in case we get an interrupt
     261                State stateChange = state;
     262                state = State.INTERRUPTED;
     263                try {
     264                    switch (command.command()) {
     265                    case PLAY:
     266                        double offset = command.offset();
     267                        speed = command.speed();
     268                        if (playingUrl != command.url() ||
     269                            stateChange != State.PAUSED ||
     270                            offset != 0.0)
     271                        {
     272                            if (audioInputStream != null) {
     273                                audioInputStream.close();
     274                                audioInputStream = null;
     275                            }
     276                            playingUrl = command.url();
     277                            audioInputStream = AudioSystem.getAudioInputStream(playingUrl);
     278                            audioFormat = audioInputStream.getFormat();
     279                            long nBytesRead = 0;
     280                            position = 0.0;
     281                            offset -= leadIn;
     282                            double calibratedOffset = offset * calibration;
     283                            bytesPerSecond = audioFormat.getFrameRate() /* frames per second */
     284                                * audioFormat.getFrameSize() /* bytes per frame */;
     285                            if (speed * bytesPerSecond > 256000.0)
     286                                speed = 256000 / bytesPerSecond;
     287                            if (calibratedOffset > 0.0) {
     288                                long bytesToSkip = (long)(
     289                                        calibratedOffset /* seconds (double) */ * bytesPerSecond);
     290                                /* skip doesn't seem to want to skip big chunks, so
     291                                 * reduce it to smaller ones
     292                                */
     293                                // audioInputStream.skip(bytesToSkip);
     294                                while (bytesToSkip > chunk) {
     295                                    nBytesRead = audioInputStream.skip(chunk);
     296                                    if (nBytesRead <= 0)
     297                                        throw new IOException(tr("This is after the end of the recording"));
     298                                    bytesToSkip -= nBytesRead;
     299                                }
     300                                if (bytesToSkip > 0)
     301                                    audioInputStream.skip(bytesToSkip);
     302                                position = offset;
     303                            }
     304                            if (audioOutputLine != null)
     305                                audioOutputLine.close();
     306                            audioFormat = new AudioFormat(audioFormat.getEncoding(),
     307                                        audioFormat.getSampleRate() * (float) (speed * calibration),
     308                                        audioFormat.getSampleSizeInBits(),
     309                                        audioFormat.getChannels(),
     310                                        audioFormat.getFrameSize(),
     311                                        audioFormat.getFrameRate() * (float) (speed * calibration),
     312                                        audioFormat.isBigEndian());
     313                            DataLine.Info info = new DataLine.Info(SourceDataLine.class, audioFormat);
     314                            audioOutputLine = (SourceDataLine) AudioSystem.getLine(info);
     315                            audioOutputLine.open(audioFormat);
     316                            audioOutputLine.start();
     317                        }
     318                        stateChange = State.PLAYING;
     319                        break;
     320                    case PAUSE:
     321                        stateChange = State.PAUSED;
     322                        break;
     323                    }
     324                    command.ok(stateChange);
     325                } catch (Exception startPlayingException) {
     326                    command.failed(startPlayingException); // sets state
     327                }
     328            } catch (Exception e) {
     329                state = State.NOTPLAYING;
     330            }
     331        }
     332    }
     333
     334    public static void audioMalfunction(Exception ex) {
     335        JOptionPane.showMessageDialog(Main.parent,
     336                "<html><p>" + tr(ex.getMessage()) + "</p></html>",
     337                tr("Error playing sound"), JOptionPane.ERROR_MESSAGE);
     338    }
    339339}
  • trunk/src/org/openstreetmap/josm/tools/AutoCompleteComboBox.java

    r741 r1169  
    1717public class AutoCompleteComboBox extends JComboBox {
    1818
    19         /**
    20         * Auto-complete a JComboBox.
    21         *
    22         * Inspired by http://www.orbital-computer.de/JComboBox/
    23         */
    24         private class AutoCompleteComboBoxDocument extends PlainDocument {
    25                 private JComboBox comboBox;
    26                 private boolean selecting = false;
     19    /**
     20    * Auto-complete a JComboBox.
     21    *
     22    * Inspired by http://www.orbital-computer.de/JComboBox/
     23    */
     24    private class AutoCompleteComboBoxDocument extends PlainDocument {
     25        private JComboBox comboBox;
     26        private boolean selecting = false;
    2727
    28                 public AutoCompleteComboBoxDocument(final JComboBox comboBox) {
    29                         this.comboBox = comboBox;
    30                 }
     28        public AutoCompleteComboBoxDocument(final JComboBox comboBox) {
     29            this.comboBox = comboBox;
     30        }
    3131
    32                 @Override public void remove(int offs, int len) throws BadLocationException {
    33                         if (selecting)
    34                                 return;
    35                         super.remove(offs, len);
    36                 }
     32        @Override public void remove(int offs, int len) throws BadLocationException {
     33            if (selecting)
     34                return;
     35            super.remove(offs, len);
     36        }
    3737
    38                 @Override public void insertString(int offs, String str, AttributeSet a) throws BadLocationException {
    39                         if(selecting || (offs == 0 && str.equals(getText(0, getLength()))))
    40                                 return;
    41                         boolean initial = (offs == 0 && getLength() == 0 && str.length() > 1);
    42                         super.insertString(offs, str, a);
     38        @Override public void insertString(int offs, String str, AttributeSet a) throws BadLocationException {
     39            if(selecting || (offs == 0 && str.equals(getText(0, getLength()))))
     40                return;
     41            boolean initial = (offs == 0 && getLength() == 0 && str.length() > 1);
     42            super.insertString(offs, str, a);
    4343
    44                         // return immediately when selecting an item
    45                         // Note: this is done after calling super method because we need
    46                         // ActionListener informed
    47                         if (selecting)
    48                                 return;
     44            // return immediately when selecting an item
     45            // Note: this is done after calling super method because we need
     46            // ActionListener informed
     47            if (selecting)
     48                return;
    4949
    50                         int size = getLength();
    51                         int start = offs+str.length();
    52                         int end = start;
    53                         String curText = getText(0, size);
    54                         // lookup and select a matching item
    55                         Object item = lookupItem(curText);
    56                         setSelectedItem(item);
    57                         if(initial)
    58                                 start = 0;
    59                         if (item != null) {
    60                                 String newText = item.toString();
    61                                 if(!newText.equals(curText))
    62                                 {
    63                                         selecting = true;
    64                                         super.remove(0, size);
    65                                         super.insertString(0, newText, a);
    66                                         selecting = false;
    67                                         start = size;
    68                                         end = getLength();
    69                                 }
    70                         }
    71                         JTextComponent editor = (JTextComponent)comboBox.getEditor().getEditorComponent();
    72                         editor.setSelectionStart(start);
    73                         editor.setSelectionEnd(end);
    74                 }
     50            int size = getLength();
     51            int start = offs+str.length();
     52            int end = start;
     53            String curText = getText(0, size);
     54            // lookup and select a matching item
     55            Object item = lookupItem(curText);
     56            setSelectedItem(item);
     57            if(initial)
     58                start = 0;
     59            if (item != null) {
     60                String newText = item.toString();
     61                if(!newText.equals(curText))
     62                {
     63                    selecting = true;
     64                    super.remove(0, size);
     65                    super.insertString(0, newText, a);
     66                    selecting = false;
     67                    start = size;
     68                    end = getLength();
     69                }
     70            }
     71            JTextComponent editor = (JTextComponent)comboBox.getEditor().getEditorComponent();
     72            editor.setSelectionStart(start);
     73            editor.setSelectionEnd(end);
     74        }
    7575
    76                 private void setSelectedItem(Object item) {
    77                         selecting = true;
    78                         comboBox.setSelectedItem(item);
    79                         selecting = false;
    80                 }
     76        private void setSelectedItem(Object item) {
     77            selecting = true;
     78            comboBox.setSelectedItem(item);
     79            selecting = false;
     80        }
    8181
    82                 private Object lookupItem(String pattern) {
    83                         ComboBoxModel model = comboBox.getModel();
    84                         for (int i = 0, n = model.getSize(); i < n; i++) {
    85                                 Object currentItem = model.getElementAt(i);
    86                                 if (currentItem.toString().startsWith(pattern))
    87                                         return currentItem;
    88                         }
    89                         return null;
    90                 }
    91         }
     82        private Object lookupItem(String pattern) {
     83            ComboBoxModel model = comboBox.getModel();
     84            for (int i = 0, n = model.getSize(); i < n; i++) {
     85                Object currentItem = model.getElementAt(i);
     86                if (currentItem.toString().startsWith(pattern))
     87                    return currentItem;
     88            }
     89            return null;
     90        }
     91    }
    9292
    93         public AutoCompleteComboBox() {
    94                 JTextComponent editor = (JTextComponent) this.getEditor().getEditorComponent();
    95                 editor.setDocument(new AutoCompleteComboBoxDocument(this));
    96         }
     93    public AutoCompleteComboBox() {
     94        JTextComponent editor = (JTextComponent) this.getEditor().getEditorComponent();
     95        editor.setDocument(new AutoCompleteComboBoxDocument(this));
     96    }
    9797
    98         public void setPossibleItems(Collection<String> elems) {
    99                 DefaultComboBoxModel model = (DefaultComboBoxModel)this.getModel();
    100                 Object oldValue = this.getEditor().getItem();
    101                 model.removeAllElements();
    102                 for (String elem : elems) model.addElement(elem);
    103                 this.getEditor().setItem(oldValue);
    104         }
     98    public void setPossibleItems(Collection<String> elems) {
     99        DefaultComboBoxModel model = (DefaultComboBoxModel)this.getModel();
     100        Object oldValue = this.getEditor().getItem();
     101        model.removeAllElements();
     102        for (String elem : elems) model.addElement(elem);
     103        this.getEditor().setItem(oldValue);
     104    }
    105105}
  • trunk/src/org/openstreetmap/josm/tools/BugReportExceptionHandler.java

    r1032 r1169  
    3939public final class BugReportExceptionHandler implements Thread.UncaughtExceptionHandler {
    4040
    41         public void uncaughtException(Thread t, Throwable e) {
    42                 e.printStackTrace();
    43                 if (Main.parent != null) {
    44                         if (e instanceof OutOfMemoryError) {
    45                                 // do not translate the string, as translation may raise an exception
    46                                 JOptionPane.showMessageDialog(Main.parent, "JOSM is out of memory. " +
    47                                                 "Strange things may happen.\nPlease restart JOSM with the -Xmx###M option,\n" +
    48                                                 "where ### is the the number of MB assigned to JOSM (e.g. 256).\n" +
    49                                                 "Currently, " + Runtime.getRuntime().maxMemory()/1024/1024 + " MB are available to JOSM.");
    50                                 return;
    51                         }
     41    public void uncaughtException(Thread t, Throwable e) {
     42        e.printStackTrace();
     43        if (Main.parent != null) {
     44            if (e instanceof OutOfMemoryError) {
     45                // do not translate the string, as translation may raise an exception
     46                JOptionPane.showMessageDialog(Main.parent, "JOSM is out of memory. " +
     47                        "Strange things may happen.\nPlease restart JOSM with the -Xmx###M option,\n" +
     48                        "where ### is the the number of MB assigned to JOSM (e.g. 256).\n" +
     49                        "Currently, " + Runtime.getRuntime().maxMemory()/1024/1024 + " MB are available to JOSM.");
     50                return;
     51            }
    5252
    53                         PluginProxy plugin = null;
     53            PluginProxy plugin = null;
    5454
    55                         // Check for an explicit problem when calling a plugin function
    56                         if (e instanceof PluginException)
    57                                 plugin = ((PluginException)e).plugin;
     55            // Check for an explicit problem when calling a plugin function
     56            if (e instanceof PluginException)
     57                plugin = ((PluginException)e).plugin;
    5858
    59                         if (plugin == null)
    60                                 plugin = guessPlugin(e);
     59            if (plugin == null)
     60                plugin = guessPlugin(e);
    6161
    62                         if (plugin != null) {
    63                                 int answer = JOptionPane.showConfirmDialog(
    64                                                 Main.parent, tr("An unexpected exception occurred that may have come from the ''{0}'' plugin.",
    65                                                 plugin.info.name) + "\n"+ (plugin.info.author != null ?
    66                                                 tr("According to the information within the plugin, the author is {0}.",
    67                                                 plugin.info.author) : "") + "\n" +
    68                                                 tr("Try updating to the newest version of this plugin before reporting a bug.") + "\n" +
    69                                                 tr("Should the plugin be disabled?"),
    70                                                 tr("Disable plugin"),
    71                                                 JOptionPane.YES_NO_OPTION);
    72                                 if (answer == JOptionPane.OK_OPTION) {
    73                                         LinkedList<String> plugins = new LinkedList<String>(Arrays.asList(Main.pref.get("plugins").split(",")));
    74                                         if (plugins.contains(plugin.info.name)) {
    75                                                 while (plugins.remove(plugin.info.name)) {}
    76                                                 String p = "";
    77                                                 for (String s : plugins)
    78                                                         p += ","+s;
    79                                                 if (p.length() > 0)
    80                                                         p = p.substring(1);
    81                                                 Main.pref.put("plugins", p);
    82                                                 JOptionPane.showMessageDialog(Main.parent,
    83                                                 tr("The plugin has been removed from the configuration. Please restart JOSM to unload the plugin."));
    84                                         } else {
    85                                                 JOptionPane.showMessageDialog(Main.parent,
    86                                                 tr("The plugin could not be removed. Please tell the people you got JOSM from about the problem."));
    87                                         }
    88                                         return;
    89                                 }
    90                         }
     62            if (plugin != null) {
     63                int answer = JOptionPane.showConfirmDialog(
     64                        Main.parent, tr("An unexpected exception occurred that may have come from the ''{0}'' plugin.",
     65                        plugin.info.name) + "\n"+ (plugin.info.author != null ?
     66                        tr("According to the information within the plugin, the author is {0}.",
     67                        plugin.info.author) : "") + "\n" +
     68                        tr("Try updating to the newest version of this plugin before reporting a bug.") + "\n" +
     69                        tr("Should the plugin be disabled?"),
     70                        tr("Disable plugin"),
     71                        JOptionPane.YES_NO_OPTION);
     72                if (answer == JOptionPane.OK_OPTION) {
     73                    LinkedList<String> plugins = new LinkedList<String>(Arrays.asList(Main.pref.get("plugins").split(",")));
     74                    if (plugins.contains(plugin.info.name)) {
     75                        while (plugins.remove(plugin.info.name)) {}
     76                        String p = "";
     77                        for (String s : plugins)
     78                            p += ","+s;
     79                        if (p.length() > 0)
     80                            p = p.substring(1);
     81                        Main.pref.put("plugins", p);
     82                        JOptionPane.showMessageDialog(Main.parent,
     83                        tr("The plugin has been removed from the configuration. Please restart JOSM to unload the plugin."));
     84                    } else {
     85                        JOptionPane.showMessageDialog(Main.parent,
     86                        tr("The plugin could not be removed. Please tell the people you got JOSM from about the problem."));
     87                    }
     88                    return;
     89                }
     90            }
    9191
    92                         Object[] options = new String[]{tr("Do nothing"), tr("Report Bug")};
    93                         int answer = JOptionPane.showOptionDialog(Main.parent, tr("An unexpected exception occurred.\n\n" +
    94                         "This is always a coding error. If you are running the latest\n" +
    95                         "version of JOSM, please consider being kind and file a bug report."),
    96                         tr("Unexpected Exception"), JOptionPane.YES_NO_OPTION, JOptionPane.ERROR_MESSAGE,
    97                         null, options, options[0]);
    98                         if (answer == 1) {
    99                                 try {
    100                                         StringWriter stack = new StringWriter();
    101                                         e.printStackTrace(new PrintWriter(stack));
     92            Object[] options = new String[]{tr("Do nothing"), tr("Report Bug")};
     93            int answer = JOptionPane.showOptionDialog(Main.parent, tr("An unexpected exception occurred.\n\n" +
     94            "This is always a coding error. If you are running the latest\n" +
     95            "version of JOSM, please consider being kind and file a bug report."),
     96            tr("Unexpected Exception"), JOptionPane.YES_NO_OPTION, JOptionPane.ERROR_MESSAGE,
     97            null, options, options[0]);
     98            if (answer == 1) {
     99                try {
     100                    StringWriter stack = new StringWriter();
     101                    e.printStackTrace(new PrintWriter(stack));
    102102
    103                                         URL revUrl = Main.class.getResource("/REVISION");
    104                                         StringBuilder sb = new StringBuilder();
    105                                         if (revUrl == null) {
    106                                                 sb.append(tr("Development version. Unknown revision."));
    107                                                 File f = new File("org/openstreetmap/josm/Main.class");
    108                                                 if (!f.exists())
    109                                                         f = new File("bin/org/openstreetmap/josm/Main.class");
    110                                                 if (!f.exists())
    111                                                         f = new File("build/org/openstreetmap/josm/Main.class");
    112                                                 if (f.exists()) {
    113                                                         DateFormat sdf = SimpleDateFormat.getDateTimeInstance();
    114                                                         sb.append("\nMain.class build on "+sdf.format(new Date(f.lastModified())));
    115                                                         sb.append("\n");
    116                                                 }
    117                                         } else {
    118                                                 BufferedReader in = new BufferedReader(new InputStreamReader(revUrl.openStream()));
    119                                                 for (String line = in.readLine(); line != null; line = in.readLine()) {
    120                                                         sb.append(line);
    121                                                         sb.append('\n');
    122                                                 }
    123                                         }
    124                                         sb.append("\n"+stack.getBuffer().toString());
     103                    URL revUrl = Main.class.getResource("/REVISION");
     104                    StringBuilder sb = new StringBuilder();
     105                    if (revUrl == null) {
     106                        sb.append(tr("Development version. Unknown revision."));
     107                        File f = new File("org/openstreetmap/josm/Main.class");
     108                        if (!f.exists())
     109                            f = new File("bin/org/openstreetmap/josm/Main.class");
     110                        if (!f.exists())
     111                            f = new File("build/org/openstreetmap/josm/Main.class");
     112                        if (f.exists()) {
     113                            DateFormat sdf = SimpleDateFormat.getDateTimeInstance();
     114                            sb.append("\nMain.class build on "+sdf.format(new Date(f.lastModified())));
     115                            sb.append("\n");
     116                        }
     117                    } else {
     118                        BufferedReader in = new BufferedReader(new InputStreamReader(revUrl.openStream()));
     119                        for (String line = in.readLine(); line != null; line = in.readLine()) {
     120                            sb.append(line);
     121                            sb.append('\n');
     122                        }
     123                    }
     124                    sb.append("\n"+stack.getBuffer().toString());
    125125
    126                                         JPanel p = new JPanel(new GridBagLayout());
    127                                         p.add(new JLabel(tr("<html>Please report a ticket at {0}<br>" +
    128                                         "Include your steps to get to the error (as detailed as possible)!<br>" +
    129                                         "Be sure to include the following information:</html>", "http://josm.openstreetmap.de/newticket")), GBC.eol());
    130                                         try {
    131                                                 Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new StringSelection(sb.toString()), new ClipboardOwner(){
    132                                                         public void lostOwnership(Clipboard clipboard, Transferable contents) {}
    133                                                 });
    134                                                 p.add(new JLabel(tr("(The text has already been copied to your clipboard.)")), GBC.eop());
    135                                         }
    136                                         catch (RuntimeException x) {}
     126                    JPanel p = new JPanel(new GridBagLayout());
     127                    p.add(new JLabel(tr("<html>Please report a ticket at {0}<br>" +
     128                    "Include your steps to get to the error (as detailed as possible)!<br>" +
     129                    "Be sure to include the following information:</html>", "http://josm.openstreetmap.de/newticket")), GBC.eol());
     130                    try {
     131                        Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new StringSelection(sb.toString()), new ClipboardOwner(){
     132                            public void lostOwnership(Clipboard clipboard, Transferable contents) {}
     133                        });
     134                        p.add(new JLabel(tr("(The text has already been copied to your clipboard.)")), GBC.eop());
     135                    }
     136                    catch (RuntimeException x) {}
    137137
    138                                         JTextArea info = new JTextArea(sb.toString(), 20, 60);
    139                                         info.setCaretPosition(0);
    140                                         info.setEditable(false);
    141                                         p.add(new JScrollPane(info), GBC.eop());
     138                    JTextArea info = new JTextArea(sb.toString(), 20, 60);
     139                    info.setCaretPosition(0);
     140                    info.setEditable(false);
     141                    p.add(new JScrollPane(info), GBC.eop());
    142142
    143                                         JOptionPane.showMessageDialog(Main.parent, p);
    144                                 } catch (Exception e1) {
    145                                         e1.printStackTrace();
    146                                 }
    147                         }
    148                 }
    149         }
     143                    JOptionPane.showMessageDialog(Main.parent, p);
     144                } catch (Exception e1) {
     145                    e1.printStackTrace();
     146                }
     147            }
     148        }
     149    }
    150150
    151         private PluginProxy guessPlugin(Throwable e) {
    152                 String name = guessPluginName(e);
    153                 for (PluginProxy p : Main.plugins)
    154                         if (p.info.name.equals(name))
    155                                 return p;
    156                 return null;
    157         }
     151    private PluginProxy guessPlugin(Throwable e) {
     152        String name = guessPluginName(e);
     153        for (PluginProxy p : Main.plugins)
     154            if (p.info.name.equals(name))
     155                return p;
     156        return null;
     157    }
    158158
    159         /**
    160         * Analyze the stack of the argument and return a name of a plugin, if
    161         * some known problem pattern has been found or <code>null</code>, if
    162         * the stack does not contain plugin-code.
    163         *
    164         * Note: This heuristic is not meant as discrimination against specific
    165         * plugins, but only to stop the flood of similar bug reports about plugins.
    166         * Of course, plugin writers are free to install their own version of
    167         * an exception handler with their email address listed to receive
    168         * bug reports ;-).
    169         */
    170         private String guessPluginName(Throwable e) {
    171                 for (StackTraceElement element : e.getStackTrace()) {
    172                         String c = element.getClassName();
     159    /**
     160    * Analyze the stack of the argument and return a name of a plugin, if
     161    * some known problem pattern has been found or <code>null</code>, if
     162    * the stack does not contain plugin-code.
     163    *
     164    * Note: This heuristic is not meant as discrimination against specific
     165    * plugins, but only to stop the flood of similar bug reports about plugins.
     166    * Of course, plugin writers are free to install their own version of
     167    * an exception handler with their email address listed to receive
     168    * bug reports ;-).
     169    */
     170    private String guessPluginName(Throwable e) {
     171        for (StackTraceElement element : e.getStackTrace()) {
     172            String c = element.getClassName();
    173173
    174                         if (c.contains("wmsplugin.") || c.contains(".WMSLayer"))
    175                                 return "wmsplugin";
    176                         if (c.contains("landsat.") || c.contains(".LandsatLayer"))
    177                                 return "landsat";
    178                         if (c.contains("livegps."))
    179                                 return "livegps";
    180                         if (c.contains("mappaint."))
    181                                 return "mappaint";
    182                         if (c.contains("annotationtester."))
    183                                 return "annotation-tester";
    184                         if (c.startsWith("UtilsPlugin."))
    185                                 return "UtilsPlugin";
     174            if (c.contains("wmsplugin.") || c.contains(".WMSLayer"))
     175                return "wmsplugin";
     176            if (c.contains("landsat.") || c.contains(".LandsatLayer"))
     177                return "landsat";
     178            if (c.contains("livegps."))
     179                return "livegps";
     180            if (c.contains("mappaint."))
     181                return "mappaint";
     182            if (c.contains("annotationtester."))
     183                return "annotation-tester";
     184            if (c.startsWith("UtilsPlugin."))
     185                return "UtilsPlugin";
    186186
    187                         if (c.startsWith("org.openstreetmap.josm.plugins.")) {
    188                                 String p = c.substring("org.openstreetmap.josm.plugins.".length());
    189                                 if (p.indexOf('.') != -1 && p.matches("[a-z].*")) {
    190                                         return p.substring(0,p.indexOf('.'));
    191                                 }
    192                         }
    193                 }
    194                 return null;
    195         }
     187            if (c.startsWith("org.openstreetmap.josm.plugins.")) {
     188                String p = c.substring("org.openstreetmap.josm.plugins.".length());
     189                if (p.indexOf('.') != -1 && p.matches("[a-z].*")) {
     190                    return p.substring(0,p.indexOf('.'));
     191                }
     192            }
     193        }
     194        return null;
     195    }
    196196}
  • trunk/src/org/openstreetmap/josm/tools/ColorHelper.java

    r885 r1169  
    88 */
    99public class ColorHelper {
    10        
    11         public static Color html2color(String html) {
    12                 if (html.length() > 0 && html.charAt(0) == '#')
    13                         html = html.substring(1);
    14                 else if (html.length() != 6 && html.length() != 8)
    15                         return null;
    16                 try {
    17                         return new Color(
    18                                         Integer.parseInt(html.substring(0,2),16),
    19                                         Integer.parseInt(html.substring(2,4),16),
    20                                         Integer.parseInt(html.substring(4,6),16),
    21                                         (html.length() == 8 ? Integer.parseInt(html.substring(6,8),16) : 255));
    22                 } catch (NumberFormatException e) {
    23                         return null;
    24                 }               
    25         }
    2610
    27         private static String int2hex(int i) {
    28                 String s = Integer.toHexString(i / 16) + Integer.toHexString(i % 16);
    29                 return s.toUpperCase();
    30         }
    31        
    32         public static String color2html(Color col) {
    33                 return "#"+int2hex(col.getRed())+int2hex(col.getGreen())+int2hex(col.getBlue());
    34         }
     11    public static Color html2color(String html) {
     12        if (html.length() > 0 && html.charAt(0) == '#')
     13            html = html.substring(1);
     14        else if (html.length() != 6 && html.length() != 8)
     15            return null;
     16        try {
     17            return new Color(
     18                    Integer.parseInt(html.substring(0,2),16),
     19                    Integer.parseInt(html.substring(2,4),16),
     20                    Integer.parseInt(html.substring(4,6),16),
     21                    (html.length() == 8 ? Integer.parseInt(html.substring(6,8),16) : 255));
     22        } catch (NumberFormatException e) {
     23            return null;
     24        }
     25    }
     26
     27    private static String int2hex(int i) {
     28        String s = Integer.toHexString(i / 16) + Integer.toHexString(i % 16);
     29        return s.toUpperCase();
     30    }
     31
     32    public static String color2html(Color col) {
     33        return "#"+int2hex(col.getRed())+int2hex(col.getGreen())+int2hex(col.getBlue());
     34    }
    3535}
  • trunk/src/org/openstreetmap/josm/tools/DateParser.java

    r627 r1169  
    88/**
    99 * Tries to parse a date as good as it can.
    10  * 
     10 *
    1111 * @author Immanuel.Scholz
    1212 */
    1313public class DateParser {
    14         public static Date parse(String d) throws ParseException {
    15                 return new PrimaryDateParser().parse(d);
    16         }
     14    public static Date parse(String d) throws ParseException {
     15        return new PrimaryDateParser().parse(d);
     16    }
    1717}
  • trunk/src/org/openstreetmap/josm/tools/Destroyable.java

    r627 r1169  
    66 * been removed) have an definite set of actions to execute. This is the "destructor" interface called
    77 * on those objects.
    8  * 
     8 *
    99 * @author immanuel.scholz
    1010 */
    1111public interface Destroyable {
    1212
    13         /**
    14         * Called when the object has been destroyed.
    15         */
    16         public void destroy();
     13    /**
     14    * Called when the object has been destroyed.
     15    */
     16    public void destroy();
    1717}
  • trunk/src/org/openstreetmap/josm/tools/DontShowAgainInfo.java

    r1004 r1169  
    1616public class DontShowAgainInfo {
    1717
    18         public static boolean show(String prefKey, String msg) {
    19                 return show(prefKey, new JLabel(msg), true, JOptionPane.OK_CANCEL_OPTION, JOptionPane.OK_OPTION);
    20         }
     18    public static boolean show(String prefKey, String msg) {
     19        return show(prefKey, new JLabel(msg), true, JOptionPane.OK_CANCEL_OPTION, JOptionPane.OK_OPTION);
     20    }
    2121
    22         public static boolean show(String prefKey, String msg, Boolean state) {
    23                 return show(prefKey, new JLabel(msg), state, JOptionPane.OK_CANCEL_OPTION, JOptionPane.OK_OPTION);
    24         }
     22    public static boolean show(String prefKey, String msg, Boolean state) {
     23        return show(prefKey, new JLabel(msg), state, JOptionPane.OK_CANCEL_OPTION, JOptionPane.OK_OPTION);
     24    }
    2525
    26         public static boolean show(String prefKey, Container msg) {
    27                 return show(prefKey, msg, true, JOptionPane.OK_CANCEL_OPTION, JOptionPane.OK_OPTION);
    28         }
     26    public static boolean show(String prefKey, Container msg) {
     27        return show(prefKey, msg, true, JOptionPane.OK_CANCEL_OPTION, JOptionPane.OK_OPTION);
     28    }
    2929
    30         public static boolean show(String prefKey, Container msg, Boolean state, int options, int true_option) {
    31                 if (!Main.pref.getBoolean("message."+prefKey)) {
    32                         JCheckBox dontshowagain = new JCheckBox(tr("Do not show again"));
    33                         dontshowagain.setSelected(Main.pref.getBoolean("message."+prefKey, state));
    34                         JPanel all = new JPanel(new GridBagLayout());
    35                         all.add(msg, GBC.eop());
    36                         all.add(dontshowagain, GBC.eol());
    37                         int answer = JOptionPane.showConfirmDialog(Main.parent, all, tr("Information"), options);
    38                         if (answer != true_option)
    39                                 return false;
    40                         Main.pref.put("message."+prefKey, dontshowagain.isSelected());
    41                 }
    42                 return true;
    43         }
     30    public static boolean show(String prefKey, Container msg, Boolean state, int options, int true_option) {
     31        if (!Main.pref.getBoolean("message."+prefKey)) {
     32            JCheckBox dontshowagain = new JCheckBox(tr("Do not show again"));
     33            dontshowagain.setSelected(Main.pref.getBoolean("message."+prefKey, state));
     34            JPanel all = new JPanel(new GridBagLayout());
     35            all.add(msg, GBC.eop());
     36            all.add(dontshowagain, GBC.eol());
     37            int answer = JOptionPane.showConfirmDialog(Main.parent, all, tr("Information"), options);
     38            if (answer != true_option)
     39                return false;
     40            Main.pref.put("message."+prefKey, dontshowagain.isSelected());
     41        }
     42        return true;
     43    }
    4444}
  • trunk/src/org/openstreetmap/josm/tools/ExifReader.java

    r627 r1169  
    1818public class ExifReader {
    1919
    20         @SuppressWarnings("unchecked") public static Date readTime(File filename) throws ParseException {
    21                 Date date = null;
    22                 try {
    23                 Metadata metadata = JpegMetadataReader.readMetadata(filename);
    24                 for (Iterator<Directory> dirIt = metadata.getDirectoryIterator(); dirIt.hasNext();) {
    25                     for (Iterator<Tag> tagIt = dirIt.next().getTagIterator(); tagIt.hasNext();) {
    26                         Tag tag = tagIt.next();
    27                         if (tag.getTagType() == 0x9003)
    28                                 return DateParser.parse(tag.getDescription());
    29                         if (tag.getTagType() == 0x132 || tag.getTagType() == 0x9004)
    30                                 date = DateParser.parse(tag.getDescription());
    31                     }
    32                 }
    33                 } catch (ParseException e) {
    34                         throw e;
     20    @SuppressWarnings("unchecked") public static Date readTime(File filename) throws ParseException {
     21        Date date = null;
     22        try {
     23            Metadata metadata = JpegMetadataReader.readMetadata(filename);
     24            for (Iterator<Directory> dirIt = metadata.getDirectoryIterator(); dirIt.hasNext();) {
     25                for (Iterator<Tag> tagIt = dirIt.next().getTagIterator(); tagIt.hasNext();) {
     26                    Tag tag = tagIt.next();
     27                    if (tag.getTagType() == 0x9003)
     28                        return DateParser.parse(tag.getDescription());
     29                    if (tag.getTagType() == 0x132 || tag.getTagType() == 0x9004)
     30                        date = DateParser.parse(tag.getDescription());
     31                }
     32            }
     33        } catch (ParseException e) {
     34            throw e;
    3535        } catch (Exception e) {
    36                 e.printStackTrace();
     36            e.printStackTrace();
    3737        }
    38                 return date;
    39         }
     38        return date;
     39    }
    4040}
  • trunk/src/org/openstreetmap/josm/tools/FallbackDateParser.java

    r627 r1169  
    1313 * based on similar code in JOSM. This class is not threadsafe, a separate
    1414 * instance must be created per thread.
    15  * 
     15 *
    1616 * @author Brett Henderson
    1717 */
    1818public class FallbackDateParser {
    19        
    20         private static final String[] formats = {
    21             "yyyy-MM-dd'T'HH:mm:ss'Z'",
    22                 "yyyy-MM-dd'T'HH:mm:ssZ",
    23                 "yyyy-MM-dd'T'HH:mm:ss",
    24                 "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'",
    25                 "yyyy-MM-dd'T'HH:mm:ss.SSSZ",
    26                 "yyyy-MM-dd HH:mm:ss",
    27                 "MM/dd/yyyy HH:mm:ss",
    28                 "MM/dd/yyyy'T'HH:mm:ss.SSS'Z'",
    29                 "MM/dd/yyyy'T'HH:mm:ss.SSSZ",
    30                 "MM/dd/yyyy'T'HH:mm:ss.SSS",
    31                 "MM/dd/yyyy'T'HH:mm:ssZ",
    32                 "MM/dd/yyyy'T'HH:mm:ss",
    33                 "yyyy:MM:dd HH:mm:ss"
    34         };
    35        
    36        
    37         private List<DateFormat> dateParsers;
    38         private int activeDateParser;
    39        
    40        
    41         /**
    42         * Creates a new instance.
    43         */
    44         public FallbackDateParser() {
    45                 // Build a list of candidate date parsers.
    46                 dateParsers = new ArrayList<DateFormat>(formats.length);
    47                 for (int i = 0; i < formats.length; i++) {
    48                         dateParsers.add(new SimpleDateFormat(formats[i]));
    49                 }
    50                
    51                 // We haven't selected a date parser yet.
    52                 activeDateParser = -1;
    53         }
    54        
    55        
    56         /**
    57         * Attempts to parse the specified date.
    58          *
    59         * @param date
    60         *            The date to parse.
    61         * @return The date.
    62         * @throws ParseException
    63         *             Occurs if the date does not match any of the supported date
    64         *             formats.
    65         */
    66         public Date parse(String date) throws ParseException {
    67                 String correctedDate;
    68                
    69                 // Try to fix ruby's broken xmlschema - format
    70                 // Replace this:
    71                 // 2007-02-12T18:43:01+00:00
    72                 // With this:
    73                 // 2007-02-12T18:43:01+0000
    74                 if (date.length() == 25 && date.charAt(22) == ':') {
    75                         correctedDate = date.substring(0, 22) + date.substring(23, 25);
    76                 } else {
    77                         correctedDate = date;
    78                 }
    79                
    80                 // If we have previously successfully used a date parser, we'll try it
    81                 // first.
    82                 if (activeDateParser >= 0) {
    83                         try {
    84                                 return dateParsers.get(activeDateParser).parse(correctedDate);
    85                         } catch (ParseException e) {
    86                                 // The currently active parser didn't work, so we must clear it
    87                                 // and find a new appropriate parser.
    88                                 activeDateParser = -1;
    89                         }
    90                 }
    91                
    92                 // Try the date parsers one by one until a suitable format is found.
    93                 for (int i = 0; i < dateParsers.size(); i++) {
    94                         try {
    95                                 Date result;
    96                                
    97                                 // Attempt to parse with the current parser, if successful we
    98                                 // store its index for next time.
    99                                 result = dateParsers.get(i).parse(correctedDate);
    100                                 activeDateParser = i;
    101                                
    102                                 return result;
    103                                
    104                         } catch (ParseException pe) {
    105                                 // Ignore parsing errors and try the next pattern.
    106                         }
    107                 }
    108                
    109                 throw new ParseException("The date string (" + date + ") could not be parsed.", 0);
    110         }
     19
     20    private static final String[] formats = {
     21        "yyyy-MM-dd'T'HH:mm:ss'Z'",
     22        "yyyy-MM-dd'T'HH:mm:ssZ",
     23        "yyyy-MM-dd'T'HH:mm:ss",
     24        "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'",
     25        "yyyy-MM-dd'T'HH:mm:ss.SSSZ",
     26        "yyyy-MM-dd HH:mm:ss",
     27        "MM/dd/yyyy HH:mm:ss",
     28        "MM/dd/yyyy'T'HH:mm:ss.SSS'Z'",
     29        "MM/dd/yyyy'T'HH:mm:ss.SSSZ",
     30        "MM/dd/yyyy'T'HH:mm:ss.SSS",
     31        "MM/dd/yyyy'T'HH:mm:ssZ",
     32        "MM/dd/yyyy'T'HH:mm:ss",
     33        "yyyy:MM:dd HH:mm:ss"
     34    };
     35
     36
     37    private List<DateFormat> dateParsers;
     38    private int activeDateParser;
     39
     40
     41    /**
     42    * Creates a new instance.
     43    */
     44    public FallbackDateParser() {
     45        // Build a list of candidate date parsers.
     46        dateParsers = new ArrayList<DateFormat>(formats.length);
     47        for (int i = 0; i < formats.length; i++) {
     48            dateParsers.add(new SimpleDateFormat(formats[i]));
     49        }
     50
     51        // We haven't selected a date parser yet.
     52        activeDateParser = -1;
     53    }
     54
     55
     56    /**
     57    * Attempts to parse the specified date.
     58     *
     59    * @param date
     60    *            The date to parse.
     61    * @return The date.
     62    * @throws ParseException
     63    *             Occurs if the date does not match any of the supported date
     64    *             formats.
     65    */
     66    public Date parse(String date) throws ParseException {
     67        String correctedDate;
     68
     69        // Try to fix ruby's broken xmlschema - format
     70        // Replace this:
     71        // 2007-02-12T18:43:01+00:00
     72        // With this:
     73        // 2007-02-12T18:43:01+0000
     74        if (date.length() == 25 && date.charAt(22) == ':') {
     75            correctedDate = date.substring(0, 22) + date.substring(23, 25);
     76        } else {
     77            correctedDate = date;
     78        }
     79
     80        // If we have previously successfully used a date parser, we'll try it
     81        // first.
     82        if (activeDateParser >= 0) {
     83            try {
     84                return dateParsers.get(activeDateParser).parse(correctedDate);
     85            } catch (ParseException e) {
     86                // The currently active parser didn't work, so we must clear it
     87                // and find a new appropriate parser.
     88                activeDateParser = -1;
     89            }
     90        }
     91
     92        // Try the date parsers one by one until a suitable format is found.
     93        for (int i = 0; i < dateParsers.size(); i++) {
     94            try {
     95                Date result;
     96
     97                // Attempt to parse with the current parser, if successful we
     98                // store its index for next time.
     99                result = dateParsers.get(i).parse(correctedDate);
     100                activeDateParser = i;
     101
     102                return result;
     103
     104            } catch (ParseException pe) {
     105                // Ignore parsing errors and try the next pattern.
     106            }
     107        }
     108
     109        throw new ParseException("The date string (" + date + ") could not be parsed.", 0);
     110    }
    111111}
  • trunk/src/org/openstreetmap/josm/tools/GBC.java

    r627 r1169  
    1212 * A wrapper for GridBagConstraints which has sane default static creators and
    1313 * member functions to chain calling.
    14  * 
     14 *
    1515 * @author imi
    1616 */
    1717public class GBC extends GridBagConstraints {
    1818
    19         /**
    20         * Use public static creator functions to create an GBC.
    21         */
    22         private GBC() {}
     19    /**
     20    * Use public static creator functions to create an GBC.
     21    */
     22    private GBC() {}
    2323
    24         /**
    25         * Create a standard constraint (which is not the last).
    26         * @return A standard constraint with no filling.
    27         */
    28         public static GBC std() {
    29                 GBC c = new GBC();
    30                 c.anchor = WEST;
    31                 return c;
    32         }
     24    /**
     25    * Create a standard constraint (which is not the last).
     26    * @return A standard constraint with no filling.
     27    */
     28    public static GBC std() {
     29        GBC c = new GBC();
     30        c.anchor = WEST;
     31        return c;
     32    }
    3333
    34         /**
    35         * Create the constraint for the last elements on a line.
    36         * @return A constraint which indicates the last item on a line.
    37         */
    38         public static GBC eol() {
    39                 GBC c = std();
    40                 c.gridwidth = REMAINDER;
    41                 return c;
    42         }
     34    /**
     35    * Create the constraint for the last elements on a line.
     36    * @return A constraint which indicates the last item on a line.
     37    */
     38    public static GBC eol() {
     39        GBC c = std();
     40        c.gridwidth = REMAINDER;
     41        return c;
     42    }
    4343
    44         /**
    45         * Create the constraint for the last elements on a line and on a paragraph.
    46         * This is merely a shortcut for eol().insets(0,0,0,10)
    47         * @return A constraint which indicates the last item on a line.
    48         */
    49         public static GBC eop() {
    50                 return eol().insets(0,0,0,10);
    51         }
     44    /**
     45    * Create the constraint for the last elements on a line and on a paragraph.
     46    * This is merely a shortcut for eol().insets(0,0,0,10)
     47    * @return A constraint which indicates the last item on a line.
     48    */
     49    public static GBC eop() {
     50        return eol().insets(0,0,0,10);
     51    }
    5252
    53         /**
    54         * Try to fill both, horizontal and vertical
    55         * @return This constraint for chaining.
    56         */
    57         public GBC fill() {
    58                 return fill(BOTH);
    59         }
     53    /**
     54    * Try to fill both, horizontal and vertical
     55    * @return This constraint for chaining.
     56    */
     57    public GBC fill() {
     58        return fill(BOTH);
     59    }
    6060
    61         /**
    62         * Set fill to the given value
    63         * @param value The filling value, either NONE, HORIZONTAL, VERTICAL or BOTH
    64         * @return This constraint for chaining.
    65         */
    66         public GBC fill(int value) {
    67                 fill = value;
    68                 if (value == HORIZONTAL || value == BOTH)
    69                         weightx = 1.0;
    70                 if (value == VERTICAL || value == BOTH)
    71                         weighty = 1.0;
    72                 return this;
    73         }
     61    /**
     62    * Set fill to the given value
     63    * @param value The filling value, either NONE, HORIZONTAL, VERTICAL or BOTH
     64    * @return This constraint for chaining.
     65    */
     66    public GBC fill(int value) {
     67        fill = value;
     68        if (value == HORIZONTAL || value == BOTH)
     69            weightx = 1.0;
     70        if (value == VERTICAL || value == BOTH)
     71            weighty = 1.0;
     72        return this;
     73    }
    7474
    75         /**
    76         * Set the anchor of this GBC to a.
    77         * @param a The new anchor, e.g. GBC.CENTER or GBC.EAST.
    78         * @return This constraint for chaining.
    79         */
    80         public GBC anchor(int a) {
    81                 anchor = a;
    82                 return this;
    83         }
     75    /**
     76    * Set the anchor of this GBC to a.
     77    * @param a The new anchor, e.g. GBC.CENTER or GBC.EAST.
     78    * @return This constraint for chaining.
     79    */
     80    public GBC anchor(int a) {
     81        anchor = a;
     82        return this;
     83    }
    8484
    85         /**
    86         * Adds insets to this GBC.
    87          * @param left          The left space of the insets
    88          * @param top           The top space of the insets
    89          * @param right         The right space of the insets
    90          * @param bottom        The bottom space of the insets
    91         * @return This constraint for chaining.
    92         */
    93         public GBC insets(int left, int top, int right, int bottom) {
    94                 insets = new Insets(top, left, bottom, right);
    95                 return this;
    96         }
     85    /**
     86    * Adds insets to this GBC.
     87     * @param left      The left space of the insets
     88     * @param top       The top space of the insets
     89     * @param right     The right space of the insets
     90     * @param bottom    The bottom space of the insets
     91    * @return This constraint for chaining.
     92    */
     93    public GBC insets(int left, int top, int right, int bottom) {
     94        insets = new Insets(top, left, bottom, right);
     95        return this;
     96    }
    9797
    98         /**
    99         * This is a helper to easily create a glue with a minimum default value.
    100         * @param x If higher than 0, this will be a horizontal glue with x as minimum
    101          *              horizontal strut.
    102         * @param y If higher than 0, this will be a vertical glue with y as minimum
    103          *              vertical strut.
    104         */
    105         public static Component glue(int x, int y) {
    106                 short maxx = x > 0 ? Short.MAX_VALUE : 0;
    107                 short maxy = y > 0 ? Short.MAX_VALUE : 0;
    108                 return new Box.Filler(new Dimension(x,y), new Dimension(x,y), new Dimension(maxx,maxy));
    109         }
     98    /**
     99    * This is a helper to easily create a glue with a minimum default value.
     100    * @param x If higher than 0, this will be a horizontal glue with x as minimum
     101     *      horizontal strut.
     102    * @param y If higher than 0, this will be a vertical glue with y as minimum
     103     *      vertical strut.
     104    */
     105    public static Component glue(int x, int y) {
     106        short maxx = x > 0 ? Short.MAX_VALUE : 0;
     107        short maxy = y > 0 ? Short.MAX_VALUE : 0;
     108        return new Box.Filler(new Dimension(x,y), new Dimension(x,y), new Dimension(maxx,maxy));
     109    }
    110110}
  • trunk/src/org/openstreetmap/josm/tools/I18n.java

    r1065 r1169  
    1010/**
    1111 * Internationalisation support.
    12  * 
     12 *
    1313 * @author Immanuel.Scholz
    1414 */
    1515public class I18n {
    1616
    17         /* Base name for translation data. Used for detecting available translations */
    18         private static final String TR_BASE = "org.openstreetmap.josm.i18n.Translation_";
     17    /* Base name for translation data. Used for detecting available translations */
     18    private static final String TR_BASE = "org.openstreetmap.josm.i18n.Translation_";
    1919
    20         /**
    21         * Set by MainApplication. Changes here later will probably mess up everything, because
    22         * many strings are already loaded.
    23         */
    24         public static org.xnap.commons.i18n.I18n i18n;
     20    /**
     21    * Set by MainApplication. Changes here later will probably mess up everything, because
     22    * many strings are already loaded.
     23    */
     24    public static org.xnap.commons.i18n.I18n i18n;
    2525
    26         public static final String tr(String text, Object... objects) {
    27                 if (i18n == null)
    28                         return MessageFormat.format(text, objects);
    29                 return i18n.tr(text, objects);
    30         }
     26    public static final String tr(String text, Object... objects) {
     27        if (i18n == null)
     28            return MessageFormat.format(text, objects);
     29        return i18n.tr(text, objects);
     30    }
    3131
    32         public static final String tr(String text) {
    33                 if (i18n == null)
    34                         return text;
    35                 return i18n.tr(text);
    36         }
     32    public static final String tr(String text) {
     33        if (i18n == null)
     34            return text;
     35        return i18n.tr(text);
     36    }
    3737
    38         public static final String marktr(String text) {
    39                 return text;
    40         }
     38    public static final String marktr(String text) {
     39        return text;
     40    }
    4141
    42         public static final String trn(String text, String pluralText, long n, Object... objects) {
    43                 if (i18n == null)
    44                         return n == 1 ? tr(text, objects) : tr(pluralText, objects);
    45                 return i18n.trn(text, pluralText, n, objects);
    46         }
     42    public static final String trn(String text, String pluralText, long n, Object... objects) {
     43        if (i18n == null)
     44            return n == 1 ? tr(text, objects) : tr(pluralText, objects);
     45        return i18n.trn(text, pluralText, n, objects);
     46    }
    4747
    48         public static final String trn(String text, String pluralText, long n) {
    49                 if (i18n == null)
    50                         return n == 1 ? tr(text) : tr(pluralText);
    51                 return i18n.trn(text, pluralText, n);
    52         }
     48    public static final String trn(String text, String pluralText, long n) {
     49        if (i18n == null)
     50            return n == 1 ? tr(text) : tr(pluralText);
     51        return i18n.trn(text, pluralText, n);
     52    }
    5353
    54         /**
    55         * Get a list of all available JOSM Translations.
    56         * @return an array of locale objects.
    57         */
    58         public static final Locale[] getAvailableTranslations() {
    59                 Vector<Locale> v = new Vector<Locale>();
    60                 Locale[] l = Locale.getAvailableLocales();
    61                 for (int i = 0; i < l.length; i++) {
    62                         String cn = TR_BASE + l[i];
    63                         try {
    64                                 Class.forName(cn);
    65                                 v.add(l[i]);
    66                         } catch (ClassNotFoundException e) {
    67                         }
    68                 }
    69                 l = new Locale[v.size()];
    70                 l = v.toArray(l);
    71                 Arrays.sort(l, new Comparator<Locale>() {
    72                         public int compare(Locale o1, Locale o2) {
    73                                 return o1.toString().compareTo(o2.toString());
    74                         }
    75                 });
    76                 return l;
    77         }
     54    /**
     55    * Get a list of all available JOSM Translations.
     56    * @return an array of locale objects.
     57    */
     58    public static final Locale[] getAvailableTranslations() {
     59        Vector<Locale> v = new Vector<Locale>();
     60        Locale[] l = Locale.getAvailableLocales();
     61        for (int i = 0; i < l.length; i++) {
     62            String cn = TR_BASE + l[i];
     63            try {
     64                Class.forName(cn);
     65                v.add(l[i]);
     66            } catch (ClassNotFoundException e) {
     67            }
     68        }
     69        l = new Locale[v.size()];
     70        l = v.toArray(l);
     71        Arrays.sort(l, new Comparator<Locale>() {
     72            public int compare(Locale o1, Locale o2) {
     73                return o1.toString().compareTo(o2.toString());
     74            }
     75        });
     76        return l;
     77    }
    7878}
  • trunk/src/org/openstreetmap/josm/tools/ImageProvider.java

    r991 r1169  
    3232public class ImageProvider {
    3333
    34         /**
    35         * Position of an overlay icon
    36         * @author imi
    37         */
    38         public static enum OverlayPosition {NORTHWEST, NORTHEAST, SOUTHWEST, SOUTHEAST}
    39 
    40         /**
    41         * The icon cache
    42         */
    43         private static Map<String, Image> cache = new HashMap<String, Image>();
    44 
    45         /**
    46         * Add here all ClassLoader whose ressource should be searched.
    47         * Plugin's class loaders are added by main.
    48         */
    49         public static final List<ClassLoader> sources = new LinkedList<ClassLoader>();
    50 
    51         /**
    52         * Return an image from the specified location.
    53         *
    54          * @param subdir        The position of the directory, e.g. "layer"
    55          * @param name          The icons name (without the ending of ".png")
    56         * @return The requested Image.
    57         */
    58         public static ImageIcon get(String subdir, String name) {
    59                 ImageIcon icon = getIfAvailable(subdir, name);
    60                 if (icon == null) {
    61                         String ext = name.indexOf('.') != -1 ? "" : ".png";
    62                         throw new NullPointerException("/images/"+subdir+name+ext+" not found");
    63                 }
    64                 return icon;
    65         }
    66 
    67         public static ImageIcon getIfAvailable(String subdir, String name)
    68         {
    69                 return getIfAvailable((Collection<String>)null, null, subdir, name);
    70         }
    71         public static final ImageIcon getIfAvailable(String[] dirs, String id, String subdir, String name)
    72         {
    73                 return getIfAvailable(Arrays.asList(dirs), id, subdir, name);
    74         }
    75 
    76         /**
    77         * Like {@link #get(String)}, but does not throw and return <code>null</code>
    78         * in case of nothing is found. Use this, if the image to retrieve is optional.
    79         */
    80         public static ImageIcon getIfAvailable(Collection<String> dirs, String id, String subdir, String name)
    81         {
    82                 if (name == null)
    83                         return null;
    84                 if (subdir == null)
    85                         subdir = "";
    86                 else if (!subdir.equals(""))
    87                         subdir += "/";
    88                 String ext = name.indexOf('.') != -1 ? "" : ".png";
    89                 String full_name = subdir+name+ext;
    90                 String cache_name = full_name;
    91                 /* cache separately */
    92                 if(dirs != null && dirs.size() > 0)
    93                         cache_name = "id:"+id+":"+full_name;
    94 
    95                 Image img = cache.get(cache_name);
    96                 if (img == null) {
    97                         // getImageUrl() does a ton of "stat()" calls and gets expensive
    98                         // and redundant when you have a whole ton of objects.  So,
    99                         // index the cache by the name of the icon we're looking for
    100                         // and don't bother to create a URL unless we're actually
    101                         // creating the image.
    102                         URL path = getImageUrl(full_name, dirs);
    103                         if (path == null)
    104                                 return null;
    105                         img = Toolkit.getDefaultToolkit().createImage(path);
    106                         cache.put(cache_name, img);
    107                 }
    108        
    109                 return new ImageIcon(img);
    110         }
    111 
    112         private static URL getImageUrl(String path, String name)
    113         {
    114                 if(path.startsWith("resource://"))
    115                 {
    116                         String p = path.substring("resource://".length());
    117                         for (ClassLoader source : sources)
    118                         {
    119                                 URL res;
    120                                 if ((res = source.getResource(p+name)) != null)
    121                                         return res;
    122                         }
    123                 }
    124                 else
    125                 {
    126                         try {
    127                                 File f = new File(path, name);
    128                                 if(f.exists())
    129                                         return f.toURI().toURL();
    130                         } catch (MalformedURLException e) {}
    131                 }
    132                 return null;
    133         }
    134 
    135         private static URL getImageUrl(String imageName, Collection<String> dirs)
    136         {
    137                 URL u;
    138                 // Try passed directories first
    139                 if(dirs != null)
    140                 {
    141                         for (String name : dirs)
    142                         {
    143                                 u = getImageUrl(name, imageName);
    144                                 if(u != null) return u;
    145                         }
    146                 }
    147                 // Try user-preference directory
    148                 u = getImageUrl(Main.pref.getPreferencesDir()+"images", imageName);
    149                 if(u != null) return u;
    150 
    151                 // Try plugins and josm classloader
    152                 u = getImageUrl("resource://images/", imageName);
    153                 if(u != null) return u;
    154 
    155                 // Try all other ressource directories
    156                 for (String location : Main.pref.getAllPossiblePreferenceDirs())
    157                 {
    158                         u = getImageUrl(location+"images", imageName);
    159                         if(u != null) return u;
    160                         u = getImageUrl(location, imageName);
    161                         if(u != null) return u;
    162                 }
    163                 return null;
    164         }
    165 
    166         /**
    167         * Shortcut for get("", name);
    168         */
    169         public static ImageIcon get(String name) {
    170                 return get("", name);
    171         }
    172 
    173         public static Cursor getCursor(String name, String overlay) {
    174                 ImageIcon img = get("cursor",name);
    175                 if (overlay != null)
    176                         img = overlay(img, "cursor/modifier/"+overlay, OverlayPosition.SOUTHEAST);
    177                 Cursor c = Toolkit.getDefaultToolkit().createCustomCursor(img.getImage(),
    178                                 name.equals("crosshair") ? new Point(10,10) : new Point(3,2), "Cursor");
    179                 return c;
    180         }
    181 
    182         /**
    183         * @return an icon that represent the overlay of the two given icons. The
    184         * second icon is layed on the first relative to the given position.
    185         */
    186         public static ImageIcon overlay(Icon ground, String overlayImage, OverlayPosition pos) {
    187                 GraphicsConfiguration conf = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();
    188                 int w = ground.getIconWidth();
    189                 int h = ground.getIconHeight();
    190                 ImageIcon overlay = ImageProvider.get(overlayImage);
    191                 int wo = overlay.getIconWidth();
    192                 int ho = overlay.getIconHeight();
    193                 BufferedImage img = conf.createCompatibleImage(w,h, Transparency.TRANSLUCENT);
    194                 Graphics g = img.createGraphics();
    195                 ground.paintIcon(null, g, 0, 0);
    196                 int x = 0, y = 0;
    197                 switch (pos) {
    198                 case NORTHWEST:
    199                         x = 0;
    200                         y = 0;
    201                         break;
    202                 case NORTHEAST:
    203                         x = w-wo;
    204                         y = 0;
    205                         break;
    206                 case SOUTHWEST:
    207                         x = 0;
    208                         y = h-ho;
    209                         break;
    210                 case SOUTHEAST:
    211                         x = w-wo;
    212                         y = h-ho;
    213                         break;
    214                 }
    215                 overlay.paintIcon(null, g, x, y);
    216                 return new ImageIcon(img);
    217         }
    218 
    219         static {
    220                 try {
    221                         sources.add(ClassLoader.getSystemClassLoader());
    222                 } catch (SecurityException ex) {
    223                         sources.add(ImageProvider.class.getClassLoader());
    224                 }
    225         }
     34    /**
     35    * Position of an overlay icon
     36    * @author imi
     37    */
     38    public static enum OverlayPosition {NORTHWEST, NORTHEAST, SOUTHWEST, SOUTHEAST}
     39
     40    /**
     41    * The icon cache
     42    */
     43    private static Map<String, Image> cache = new HashMap<String, Image>();
     44
     45    /**
     46    * Add here all ClassLoader whose ressource should be searched.
     47    * Plugin's class loaders are added by main.
     48    */
     49    public static final List<ClassLoader> sources = new LinkedList<ClassLoader>();
     50
     51    /**
     52    * Return an image from the specified location.
     53    *
     54     * @param subdir    The position of the directory, e.g. "layer"
     55     * @param name      The icons name (without the ending of ".png")
     56    * @return The requested Image.
     57    */
     58    public static ImageIcon get(String subdir, String name) {
     59        ImageIcon icon = getIfAvailable(subdir, name);
     60        if (icon == null) {
     61            String ext = name.indexOf('.') != -1 ? "" : ".png";
     62            throw new NullPointerException("/images/"+subdir+name+ext+" not found");
     63        }
     64        return icon;
     65    }
     66
     67    public static ImageIcon getIfAvailable(String subdir, String name)
     68    {
     69        return getIfAvailable((Collection<String>)null, null, subdir, name);
     70    }
     71    public static final ImageIcon getIfAvailable(String[] dirs, String id, String subdir, String name)
     72    {
     73        return getIfAvailable(Arrays.asList(dirs), id, subdir, name);
     74    }
     75
     76    /**
     77    * Like {@link #get(String)}, but does not throw and return <code>null</code>
     78    * in case of nothing is found. Use this, if the image to retrieve is optional.
     79    */
     80    public static ImageIcon getIfAvailable(Collection<String> dirs, String id, String subdir, String name)
     81    {
     82        if (name == null)
     83            return null;
     84        if (subdir == null)
     85            subdir = "";
     86        else if (!subdir.equals(""))
     87            subdir += "/";
     88        String ext = name.indexOf('.') != -1 ? "" : ".png";
     89        String full_name = subdir+name+ext;
     90        String cache_name = full_name;
     91        /* cache separately */
     92        if(dirs != null && dirs.size() > 0)
     93            cache_name = "id:"+id+":"+full_name;
     94
     95        Image img = cache.get(cache_name);
     96        if (img == null) {
     97            // getImageUrl() does a ton of "stat()" calls and gets expensive
     98            // and redundant when you have a whole ton of objects.  So,
     99            // index the cache by the name of the icon we're looking for
     100            // and don't bother to create a URL unless we're actually
     101            // creating the image.
     102            URL path = getImageUrl(full_name, dirs);
     103            if (path == null)
     104                return null;
     105            img = Toolkit.getDefaultToolkit().createImage(path);
     106            cache.put(cache_name, img);
     107        }
     108
     109        return new ImageIcon(img);
     110    }
     111
     112    private static URL getImageUrl(String path, String name)
     113    {
     114        if(path.startsWith("resource://"))
     115        {
     116            String p = path.substring("resource://".length());
     117            for (ClassLoader source : sources)
     118            {
     119                URL res;
     120                if ((res = source.getResource(p+name)) != null)
     121                    return res;
     122            }
     123        }
     124        else
     125        {
     126            try {
     127                File f = new File(path, name);
     128                if(f.exists())
     129                    return f.toURI().toURL();
     130            } catch (MalformedURLException e) {}
     131        }
     132        return null;
     133    }
     134
     135    private static URL getImageUrl(String imageName, Collection<String> dirs)
     136    {
     137        URL u;
     138        // Try passed directories first
     139        if(dirs != null)
     140        {
     141            for (String name : dirs)
     142            {
     143                u = getImageUrl(name, imageName);
     144                if(u != null) return u;
     145            }
     146        }
     147        // Try user-preference directory
     148        u = getImageUrl(Main.pref.getPreferencesDir()+"images", imageName);
     149        if(u != null) return u;
     150
     151        // Try plugins and josm classloader
     152        u = getImageUrl("resource://images/", imageName);
     153        if(u != null) return u;
     154
     155        // Try all other ressource directories
     156        for (String location : Main.pref.getAllPossiblePreferenceDirs())
     157        {
     158            u = getImageUrl(location+"images", imageName);
     159            if(u != null) return u;
     160            u = getImageUrl(location, imageName);
     161            if(u != null) return u;
     162        }
     163        return null;
     164    }
     165
     166    /**
     167    * Shortcut for get("", name);
     168    */
     169    public static ImageIcon get(String name) {
     170        return get("", name);
     171    }
     172
     173    public static Cursor getCursor(String name, String overlay) {
     174        ImageIcon img = get("cursor",name);
     175        if (overlay != null)
     176            img = overlay(img, "cursor/modifier/"+overlay, OverlayPosition.SOUTHEAST);
     177        Cursor c = Toolkit.getDefaultToolkit().createCustomCursor(img.getImage(),
     178                name.equals("crosshair") ? new Point(10,10) : new Point(3,2), "Cursor");
     179        return c;
     180    }
     181
     182    /**
     183    * @return an icon that represent the overlay of the two given icons. The
     184    * second icon is layed on the first relative to the given position.
     185    */
     186    public static ImageIcon overlay(Icon ground, String overlayImage, OverlayPosition pos) {
     187        GraphicsConfiguration conf = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();
     188        int w = ground.getIconWidth();
     189        int h = ground.getIconHeight();
     190        ImageIcon overlay = ImageProvider.get(overlayImage);
     191        int wo = overlay.getIconWidth();
     192        int ho = overlay.getIconHeight();
     193        BufferedImage img = conf.createCompatibleImage(w,h, Transparency.TRANSLUCENT);
     194        Graphics g = img.createGraphics();
     195        ground.paintIcon(null, g, 0, 0);
     196        int x = 0, y = 0;
     197        switch (pos) {
     198        case NORTHWEST:
     199            x = 0;
     200            y = 0;
     201            break;
     202        case NORTHEAST:
     203            x = w-wo;
     204            y = 0;
     205            break;
     206        case SOUTHWEST:
     207            x = 0;
     208            y = h-ho;
     209            break;
     210        case SOUTHEAST:
     211            x = w-wo;
     212            y = h-ho;
     213            break;
     214        }
     215        overlay.paintIcon(null, g, x, y);
     216        return new ImageIcon(img);
     217    }
     218
     219    static {
     220        try {
     221            sources.add(ClassLoader.getSystemClassLoader());
     222        } catch (SecurityException ex) {
     223            sources.add(ImageProvider.class.getClassLoader());
     224        }
     225    }
    226226}
  • trunk/src/org/openstreetmap/josm/tools/OpenBrowser.java

    r1023 r1169  
    1919public class OpenBrowser {
    2020
    21         /**
    22         * @return <code>null</code> for success or a string in case of an error.
    23         */
    24         public static String displayUrl(String url) {
    25                 if (Main.applet) {
    26                         try {
    27                                 JApplet applet = (JApplet) Main.parent;
    28                                 applet.getAppletContext().showDocument(new URL(url));
    29                                 return null;
    30                         } catch (MalformedURLException mue) {
    31                                 return mue.getMessage();
    32                         }
    33                 }
     21    /**
     22    * @return <code>null</code> for success or a string in case of an error.
     23    */
     24    public static String displayUrl(String url) {
     25        if (Main.applet) {
     26            try {
     27                JApplet applet = (JApplet) Main.parent;
     28                applet.getAppletContext().showDocument(new URL(url));
     29                return null;
     30            } catch (MalformedURLException mue) {
     31                return mue.getMessage();
     32            }
     33        }
    3434
    35                 try {
    36                         Main.platform.openUrl(url);
    37                 } catch (IOException e) {
    38                         return e.getMessage();
    39                 }
    40                 return null;
    41         }
     35        try {
     36            Main.platform.openUrl(url);
     37        } catch (IOException e) {
     38            return e.getMessage();
     39        }
     40        return null;
     41    }
    4242
    4343}
  • trunk/src/org/openstreetmap/josm/tools/Pair.java

    r627 r1169  
    66 */
    77public final class Pair<A,B> {
    8         public A a;
    9         public B b;
     8    public A a;
     9    public B b;
    1010
    11         public Pair(A a, B b) {
    12                 this.a = a;
    13                 this.b = b;
    14         }
     11    public Pair(A a, B b) {
     12        this.a = a;
     13        this.b = b;
     14    }
    1515
    16         @Override public int hashCode() {
    17                 return a.hashCode() ^ b.hashCode();
    18         }
     16    @Override public int hashCode() {
     17        return a.hashCode() ^ b.hashCode();
     18    }
    1919
    20         @Override public boolean equals(Object o) {
    21                 return o == null ? o == null : o instanceof Pair
    22                         && a.equals(((Pair<?,?>) o).a) && b.equals(((Pair<?,?>) o).b);
    23         }
     20    @Override public boolean equals(Object o) {
     21        return o == null ? o == null : o instanceof Pair
     22            && a.equals(((Pair<?,?>) o).a) && b.equals(((Pair<?,?>) o).b);
     23    }
    2424
    25         public static <T> ArrayList<T> toArrayList(Pair<T, T> p) {
    26                 ArrayList<T> l = new ArrayList<T>(2);
    27                 l.add(p.a);
    28                 l.add(p.b);
    29                 return l;
    30         }
     25    public static <T> ArrayList<T> toArrayList(Pair<T, T> p) {
     26        ArrayList<T> l = new ArrayList<T>(2);
     27        l.add(p.a);
     28        l.add(p.b);
     29        return l;
     30    }
    3131
    32         public static <T> Pair<T,T> sort(Pair<T,T> p) {
    33                 if (p.b.hashCode() < p.a.hashCode()) {
    34                         T tmp = p.a;
    35                         p.a = p.b;
    36                         p.b = tmp;
    37                 }
    38                 return p;
    39         }
     32    public static <T> Pair<T,T> sort(Pair<T,T> p) {
     33        if (p.b.hashCode() < p.a.hashCode()) {
     34            T tmp = p.a;
     35            p.a = p.b;
     36            p.b = tmp;
     37        }
     38        return p;
     39    }
    4040}
  • trunk/src/org/openstreetmap/josm/tools/PlatformHook.java

    r1084 r1169  
    2929 */
    3030public interface PlatformHook {
    31         /**
    32           * The preStartupHook will be called extremly early. It is
    33           * guaranteed to be called before the GUI setup has started.
    34           *
    35           * Reason: On OSX we need to inform the Swing libraries
    36           * that we want to be integrated with the OS before we setup
    37           * our GUI.
    38           */
    39         public void preStartupHook();
     31    /**
     32      * The preStartupHook will be called extremly early. It is
     33      * guaranteed to be called before the GUI setup has started.
     34      *
     35      * Reason: On OSX we need to inform the Swing libraries
     36      * that we want to be integrated with the OS before we setup
     37      * our GUI.
     38      */
     39    public void preStartupHook();
    4040
    41         /**
    42           * The startupHook will be called early, but after the GUI
    43           * setup has started.
    44           *
    45           * Reason: On OSX we need to register some callbacks with the
    46           * OS, so we'll receive events from the system menu.
    47           */
    48         public void startupHook();
     41    /**
     42      * The startupHook will be called early, but after the GUI
     43      * setup has started.
     44      *
     45      * Reason: On OSX we need to register some callbacks with the
     46      * OS, so we'll receive events from the system menu.
     47      */
     48    public void startupHook();
    4949
    50         /**
    51           * The openURL hook will be used to open an URL in the
    52           * default webbrowser.
    53           */
    54         public void openUrl(String url) throws IOException;
     50    /**
     51      * The openURL hook will be used to open an URL in the
     52      * default webbrowser.
     53      */
     54    public void openUrl(String url) throws IOException;
    5555
    56         /**
    57           * The initShortcutGroups hook will be called by the
    58           * Shortcut class if it detects that there are no
    59           * groups in teh config file. So that will happen
    60           * once on each JOSM installation only.
    61           *
    62           * Please note that ShorCut will load its config on demand,
    63           * that is, at the moment the first shortcut is registered.
    64           *
    65           * In this hook, you have to fill the preferences with
    66           * data, not the internal structures! Also, do not try
    67           * to register any shortcuts from within.
    68           */
    69         public void initShortcutGroups();
     56    /**
     57      * The initShortcutGroups hook will be called by the
     58      * Shortcut class if it detects that there are no
     59      * groups in teh config file. So that will happen
     60      * once on each JOSM installation only.
     61      *
     62      * Please note that ShorCut will load its config on demand,
     63      * that is, at the moment the first shortcut is registered.
     64      *
     65      * In this hook, you have to fill the preferences with
     66      * data, not the internal structures! Also, do not try
     67      * to register any shortcuts from within.
     68      */
     69    public void initShortcutGroups();
    7070
    71         /**
    72           * The initSystemShortcuts hook will be called by the
    73           * Shortcut class after the modifier groups have been read
    74           * from the config, but before any shortcuts are read from
    75           * it or registered from within the application.
    76           *
    77           * Plese note that you are not allowed to register any
    78           * shortuts from this hook, but only "systemCuts"!
    79           *
    80           * BTW: SystemCuts should be named "system:<whatever>",
    81           * and it'd be best if sou'd recycle the names already used
    82           * by the Windows and OSX hooks. Especially the later has
    83           * really many of them.
    84           *
    85           * You should also register any and all shortcuts that the
    86           * operation system handles itself to block JOSM from trying
    87           * to use them---as that would just not work. Call setAutomatic
    88           * on them to prevent the keyboard preferences from allowing the
    89           * user to change them.
    90           */
    91         public void initSystemShortcuts();
     71    /**
     72      * The initSystemShortcuts hook will be called by the
     73      * Shortcut class after the modifier groups have been read
     74      * from the config, but before any shortcuts are read from
     75      * it or registered from within the application.
     76      *
     77      * Plese note that you are not allowed to register any
     78      * shortuts from this hook, but only "systemCuts"!
     79      *
     80      * BTW: SystemCuts should be named "system:<whatever>",
     81      * and it'd be best if sou'd recycle the names already used
     82      * by the Windows and OSX hooks. Especially the later has
     83      * really many of them.
     84      *
     85      * You should also register any and all shortcuts that the
     86      * operation system handles itself to block JOSM from trying
     87      * to use them---as that would just not work. Call setAutomatic
     88      * on them to prevent the keyboard preferences from allowing the
     89      * user to change them.
     90      */
     91    public void initSystemShortcuts();
    9292
    93         /**
    94           * The makeTooltip hook will be called whenever a tooltip for
    95           * a menu or button is created.
    96           *
    97           * Tooltips are usually not system dependent, unless the
    98           * JVM is to dumb to provide correct names for all the keys.
    99           *
    100           * Another reason not to use the implementation in the *nix
    101           * hook are LAFs that don't understand HTML, such as the OSX
    102           * LAFs.
    103           */
    104         public String makeTooltip(String name, Shortcut sc);
     93    /**
     94      * The makeTooltip hook will be called whenever a tooltip for
     95      * a menu or button is created.
     96      *
     97      * Tooltips are usually not system dependent, unless the
     98      * JVM is to dumb to provide correct names for all the keys.
     99      *
     100      * Another reason not to use the implementation in the *nix
     101      * hook are LAFs that don't understand HTML, such as the OSX
     102      * LAFs.
     103      */
     104    public String makeTooltip(String name, Shortcut sc);
    105105}
  • trunk/src/org/openstreetmap/josm/tools/PlatformHookOsx.java

    r1084 r1169  
    1616  */
    1717public class PlatformHookOsx extends PlatformHookUnixoid implements PlatformHook, InvocationHandler {
    18         private static PlatformHookOsx ivhandler = new PlatformHookOsx();
    19         public void preStartupHook(){
    20                 // This will merge our MenuBar into the system menu.
    21                 // MUST be set before Swing is initialized!
    22                 // And will not work when one of the system independet LAFs is used.
    23                 // They just insist on painting themselves...
    24                 System.setProperty("apple.laf.useScreenMenuBar", "true");
    25         }
    26         public void startupHook() {
    27                 // Here we register callbacks for the menu entries in the system menu
    28                 try {
    29                         Class Ccom_apple_eawt_Application = Class.forName("com.apple.eawt.Application");
    30                         Object Ocom_apple_eawt_Application = Ccom_apple_eawt_Application.getConstructor((Class[])null).newInstance((Object[])null);
    31                         Class Ccom_apple_eawt_ApplicationListener = Class.forName("com.apple.eawt.ApplicationListener");
    32                         Method MaddApplicationListener = Ccom_apple_eawt_Application.getDeclaredMethod("addApplicationListener", new Class[] { Ccom_apple_eawt_ApplicationListener });
    33                         Object Oproxy = Proxy.newProxyInstance(PlatformHookOsx.class.getClassLoader(), new Class[] { Ccom_apple_eawt_ApplicationListener }, ivhandler);
    34                         MaddApplicationListener.invoke(Ocom_apple_eawt_Application, new Object[] { Oproxy });
    35                         Method MsetEnabledPreferencesMenu = Ccom_apple_eawt_Application.getDeclaredMethod("setEnabledPreferencesMenu", new Class[] { boolean.class });
    36                         MsetEnabledPreferencesMenu.invoke(Ocom_apple_eawt_Application, new Object[] { Boolean.TRUE });
    37                 } catch (Exception ex) {
    38                         // Oops, what now?
    39                         // We'll just ignore this for now. The user will still be able to close JOSM
    40                         // by closing all its windows.
    41                         System.out.println("Failed to register with OSX: " + ex);
    42                 }
    43         }
    44         public Object invoke (Object proxy, Method method, Object[] args) throws Throwable {
    45                 Boolean handled = Boolean.TRUE;
    46                 //System.out.println("Going to handle method "+method+" (short: "+method.getName()+") with event "+args[0]);
    47                 if (method.getName().equals("handleQuit")) {
    48                         handled = !Main.main.breakBecauseUnsavedChanges();
    49                 } else if (method.getName().equals("handleAbout")) {
    50                         Main.main.menu.about.actionPerformed(null);
    51                 } else if (method.getName().equals("handlePreferences")) {
    52                         Main.main.menu.preferences.actionPerformed(null);
    53                 } else {
    54                         return null;
    55                 }
    56                 if (args[0] != null) {
    57                         try {
    58                                 args[0].getClass().getDeclaredMethod("setHandled", new Class[] { boolean.class }).invoke(args[0], new Object[] { handled });
    59                         } catch (Exception ex) {
    60                                 System.out.println("Failed to report handled event: " + ex);
    61                         }
    62                 }
    63                 return null;
    64         }
    65         public void openUrl(String url) throws IOException {
    66                 // Ain't that KISS?
    67                 Runtime.getRuntime().exec("open " + url);
    68         }
    69         public void initShortcutGroups() {
    70                 // Everything but Shortcut.GROUPS_DEFAULT+Shortcut.GROUP_MENU is guesswork.
    71                 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_DEFAULT+Shortcut.GROUP_NONE),    Integer.toString(-1));
    72                 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_DEFAULT+Shortcut.GROUP_HOTKEY),  Integer.toString(KeyEvent.CTRL_DOWN_MASK));
    73                 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_DEFAULT+Shortcut.GROUP_MENU),    Integer.toString(KeyEvent.META_DOWN_MASK));
    74                 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_DEFAULT+Shortcut.GROUP_EDIT),    Integer.toString(0));
    75                 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_DEFAULT+Shortcut.GROUP_LAYER),   Integer.toString(KeyEvent.ALT_DOWN_MASK));
    76                 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_DEFAULT+Shortcut.GROUP_DIRECT),  Integer.toString(0));
    77                 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_DEFAULT+Shortcut.GROUP_MNEMONIC),Integer.toString(KeyEvent.ALT_DOWN_MASK));
    78 
    79                 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT1+Shortcut.GROUP_NONE),       Integer.toString(-1));
    80                 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT1+Shortcut.GROUP_HOTKEY),     Integer.toString(KeyEvent.CTRL_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK));
    81                 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT1+Shortcut.GROUP_MENU),       Integer.toString(KeyEvent.META_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK));
    82                 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT1+Shortcut.GROUP_EDIT),       Integer.toString(KeyEvent.SHIFT_DOWN_MASK));
    83                 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT1+Shortcut.GROUP_LAYER),      Integer.toString(KeyEvent.ALT_DOWN_MASK  | KeyEvent.SHIFT_DOWN_MASK));
    84                 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT1+Shortcut.GROUP_DIRECT),     Integer.toString(KeyEvent.SHIFT_DOWN_MASK));
    85                 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT1+Shortcut.GROUP_MNEMONIC),   Integer.toString(KeyEvent.ALT_DOWN_MASK));
    86 
    87                 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT2+Shortcut.GROUP_NONE),       Integer.toString(-1));
    88                 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT2+Shortcut.GROUP_HOTKEY),     Integer.toString(KeyEvent.CTRL_DOWN_MASK | KeyEvent.ALT_DOWN_MASK));
    89                 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT2+Shortcut.GROUP_MENU),       Integer.toString(KeyEvent.META_DOWN_MASK | KeyEvent.ALT_DOWN_MASK));
    90                 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT2+Shortcut.GROUP_EDIT),       Integer.toString(KeyEvent.ALT_DOWN_MASK  | KeyEvent.SHIFT_DOWN_MASK));
    91                 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT2+Shortcut.GROUP_LAYER),      Integer.toString(KeyEvent.ALT_DOWN_MASK));
    92                 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT2+Shortcut.GROUP_DIRECT),     Integer.toString(KeyEvent.SHIFT_DOWN_MASK | KeyEvent.ALT_DOWN_MASK));
    93                 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT2+Shortcut.GROUP_MNEMONIC),   Integer.toString(KeyEvent.ALT_DOWN_MASK));
    94         }
    95         public void initSystemShortcuts() {
    96                 // Yeah, it's a long, long list. And people always complain that OSX has no shortcuts.
    97                 Shortcut.registerSystemShortcut("apple-reserved-01", "reserved", KeyEvent.VK_SPACE, KeyEvent.META_DOWN_MASK).setAutomatic(); // Show or hide the Spotlight search field (when multiple languages are installed, may rotate through enabled script systems).
    98                 Shortcut.registerSystemShortcut("apple-reserved-02", "reserved", KeyEvent.VK_SPACE, KeyEvent.META_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK).setAutomatic(); // Apple reserved.
    99                 Shortcut.registerSystemShortcut("apple-reserved-03", "reserved", KeyEvent.VK_SPACE, KeyEvent.META_DOWN_MASK | KeyEvent.ALT_DOWN_MASK).setAutomatic(); // Show the Spotlight search results window (when multiple languages are installed, may rotate through keyboard layouts and input methods within a script).
    100                 Shortcut.registerSystemShortcut("apple-reserved-04", "reserved", KeyEvent.VK_SPACE, KeyEvent.META_DOWN_MASK | KeyEvent.CTRL_DOWN_MASK).setAutomatic(); //  | Apple reserved.
    101                 Shortcut.registerSystemShortcut("apple-reserved-05", "reserved", KeyEvent.VK_TAB, KeyEvent.SHIFT_DOWN_MASK).setAutomatic(); // Navigate through controls in a reverse direction. See "Keyboard Focus and Navigation."
    102                 Shortcut.registerSystemShortcut("apple-reserved-06", "reserved", KeyEvent.VK_TAB, KeyEvent.META_DOWN_MASK).setAutomatic(); // Move forward to the next most recently used application in a list of open applications.
    103                 Shortcut.registerSystemShortcut("apple-reserved-07", "reserved", KeyEvent.VK_TAB, KeyEvent.META_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK).setAutomatic(); // Move backward through a list of open applications (sorted by recent use).
    104                 Shortcut.registerSystemShortcut("apple-reserved-08", "reserved", KeyEvent.VK_TAB, KeyEvent.CTRL_DOWN_MASK).setAutomatic(); // Move focus to the next grouping of controls in a dialog or the next table (when Tab moves to the next cell). See Accessibility Overview.
    105                 Shortcut.registerSystemShortcut("apple-reserved-09", "reserved", KeyEvent.VK_TAB, KeyEvent.CTRL_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK).setAutomatic(); // Move focus to the previous grouping of controls. See Accessibility Overview.
    106                 Shortcut.registerSystemShortcut("apple-reserved-10", "reserved", KeyEvent.VK_ESCAPE, KeyEvent.META_DOWN_MASK).setAutomatic(); // Open Front Row.
    107                 Shortcut.registerSystemShortcut("apple-reserved-11", "reserved", KeyEvent.VK_ESCAPE, KeyEvent.META_DOWN_MASK | KeyEvent.ALT_DOWN_MASK).setAutomatic(); // Open the Force Quit dialog.
    108                 Shortcut.registerSystemShortcut("apple-reserved-12", "reserved", KeyEvent.VK_F1, KeyEvent.CTRL_DOWN_MASK).setAutomatic(); // Toggle full keyboard access on or off. See Accessibility Overview.
    109                 Shortcut.registerSystemShortcut("apple-reserved-13", "reserved", KeyEvent.VK_F2, KeyEvent.CTRL_DOWN_MASK).setAutomatic(); // Move focus to the menu bar. See Accessibility Overview.
    110                 Shortcut.registerSystemShortcut("apple-reserved-14", "reserved", KeyEvent.VK_F3, KeyEvent.CTRL_DOWN_MASK).setAutomatic(); // Move focus to the Dock. See Accessibility Overview.
    111                 Shortcut.registerSystemShortcut("apple-reserved-15", "reserved", KeyEvent.VK_F4, KeyEvent.CTRL_DOWN_MASK).setAutomatic(); // Move focus to the active (or next) window. See Accessibility Overview.
    112                 Shortcut.registerSystemShortcut("apple-reserved-16", "reserved", KeyEvent.VK_F4, KeyEvent.CTRL_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK).setAutomatic(); // Move focus to the previously active window. See Accessibility Overview.
    113                 Shortcut.registerSystemShortcut("apple-reserved-17", "reserved", KeyEvent.VK_F5, KeyEvent.CTRL_DOWN_MASK).setAutomatic(); // Move focus to the toolbar. See Accessibility Overview.
    114                 Shortcut.registerSystemShortcut("apple-reserved-18", "reserved", KeyEvent.VK_F5, KeyEvent.META_DOWN_MASK).setAutomatic(); // Turn VoiceOver on or off. See Accessibility Overview.
    115                 Shortcut.registerSystemShortcut("apple-reserved-19", "reserved", KeyEvent.VK_F6, KeyEvent.CTRL_DOWN_MASK).setAutomatic(); // Move focus to the first (or next) panel. See Accessibility Overview.
    116                 Shortcut.registerSystemShortcut("apple-reserved-20", "reserved", KeyEvent.VK_F6, KeyEvent.CTRL_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK).setAutomatic(); // Move focus to the previous panel. See Accessibility Overview.
    117                 Shortcut.registerSystemShortcut("apple-reserved-21", "reserved", KeyEvent.VK_F7, KeyEvent.CTRL_DOWN_MASK).setAutomatic(); // Temporarily override the current keyboard access mode in windows and dialogs. See Accessibility Overview.
    118                 Shortcut.registerSystemShortcut("apple-reserved-22", "reserved", KeyEvent.VK_F9, 0).setAutomatic(); // Tile or untile all open windows.
    119                 Shortcut.registerSystemShortcut("apple-reserved-23", "reserved", KeyEvent.VK_F10, 0).setAutomatic(); // Tile or untile all open windows in the currently active application.
    120                 Shortcut.registerSystemShortcut("apple-reserved-24", "reserved", KeyEvent.VK_F11, 0).setAutomatic(); // Hide or show all open windows.
    121                 Shortcut.registerSystemShortcut("apple-reserved-25", "reserved", KeyEvent.VK_F12, 0).setAutomatic(); // Hide or display Dashboard.
    122                 Shortcut.registerSystemShortcut("apple-reserved-26", "reserved", KeyEvent.VK_DEAD_GRAVE, KeyEvent.META_DOWN_MASK).setAutomatic(); // Activate the next open window in the frontmost application. See "Window Layering."
    123                 Shortcut.registerSystemShortcut("apple-reserved-27", "reserved", KeyEvent.VK_DEAD_GRAVE, KeyEvent.META_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK).setAutomatic(); // Activate the previous open window in the frontmost application. See "Window Layering."
    124                 Shortcut.registerSystemShortcut("apple-reserved-28", "reserved", KeyEvent.VK_DEAD_GRAVE, KeyEvent.META_DOWN_MASK | KeyEvent.ALT_DOWN_MASK).setAutomatic(); // Move focus to the window drawer.
    125                 Shortcut.registerSystemShortcut("apple-reserved-29", "reserved", KeyEvent.VK_MINUS, KeyEvent.META_DOWN_MASK).setAutomatic(); // Decrease the size of the selected item (equivalent to the Smaller command). See "The Format Menu."
    126                 Shortcut.registerSystemShortcut("apple-reserved-30", "reserved", KeyEvent.VK_MINUS, KeyEvent.META_DOWN_MASK | KeyEvent.ALT_DOWN_MASK).setAutomatic(); // Zoom out when screen zooming is on. See Accessibility Overview.
    127 
    128                 Shortcut.registerSystemShortcut("system:align-left", "reserved", KeyEvent.VK_OPEN_BRACKET, KeyEvent.META_DOWN_MASK); // Left-align a selection (equivalent to the Align Left command). See "The Format Menu."
    129                 Shortcut.registerSystemShortcut("system:align-right","reserved", KeyEvent.VK_CLOSE_BRACKET, KeyEvent.META_DOWN_MASK); // Right-align a selection (equivalent to the Align Right command). See "The Format Menu."
    130                 // I found no KeyEvent for |
    131                 //Shortcut.registerSystemCut("system:align-center", "reserved", '|', KeyEvent.META_DOWN_MASK); // Center-align a selection (equivalent to the Align Center command). See "The Format Menu."
    132                 Shortcut.registerSystemShortcut("system:spelling", "reserved", KeyEvent.VK_COLON, KeyEvent.META_DOWN_MASK); // Display the Spelling window (equivalent to the Spelling command). See "The Edit Menu."
    133                 Shortcut.registerSystemShortcut("system:spellcheck", "reserved", KeyEvent.VK_SEMICOLON, KeyEvent.META_DOWN_MASK); // Find misspelled words in the document (equivalent to the Check Spelling command). See "The Edit Menu."
    134                 Shortcut.registerSystemShortcut("system:preferences", "reserved", KeyEvent.VK_COMMA, KeyEvent.META_DOWN_MASK).setAutomatic(); // Open the application's preferences window (equivalent to the Preferences command). See "The Application Menu."
    135 
    136                 Shortcut.registerSystemShortcut("apple-reserved-31", "reserved", KeyEvent.VK_COMMA, KeyEvent.META_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK | KeyEvent.ALT_DOWN_MASK).setAutomatic(); // Decrease screen contrast. See Accessibility Overview.
    137                 Shortcut.registerSystemShortcut("apple-reserved-32", "reserved", KeyEvent.VK_PERIOD, KeyEvent.META_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK | KeyEvent.ALT_DOWN_MASK).setAutomatic(); // Increase screen contrast. See Accessibility Overview.
    138 
    139                 // I found no KeyEvent for ?
    140                 //Shortcut.registerSystemCut("system:help", "reserved", '?', KeyEvent.META_DOWN_MASK).setAutomatic(); // Open the application's help in Help Viewer. See "The Help Menu."
    141 
    142                 Shortcut.registerSystemShortcut("apple-reserved-33", "reserved", KeyEvent.VK_SLASH, KeyEvent.META_DOWN_MASK | KeyEvent.ALT_DOWN_MASK).setAutomatic(); // Turn font smoothing on or off.
    143                 Shortcut.registerSystemShortcut("apple-reserved-34", "reserved", KeyEvent.VK_EQUALS, KeyEvent.META_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK).setAutomatic(); // Increase the size of the selected item (equivalent to the Bigger command). See "The Format Menu."
    144                 Shortcut.registerSystemShortcut("apple-reserved-35", "reserved", KeyEvent.VK_EQUALS, KeyEvent.META_DOWN_MASK | KeyEvent.ALT_DOWN_MASK).setAutomatic(); // Zoom in when screen zooming is on. See Accessibility Overview.
    145                 Shortcut.registerSystemShortcut("apple-reserved-36", "reserved", KeyEvent.VK_3, KeyEvent.META_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK).setAutomatic(); // Capture the screen to a file.
    146                 Shortcut.registerSystemShortcut("apple-reserved-37", "reserved", KeyEvent.VK_3, KeyEvent.META_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK | KeyEvent.CTRL_DOWN_MASK).setAutomatic(); // Capture the screen to the Clipboard.
    147                 Shortcut.registerSystemShortcut("apple-reserved-38", "reserved", KeyEvent.VK_4, KeyEvent.META_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK).setAutomatic(); // Capture a selection to a file.
    148                 Shortcut.registerSystemShortcut("apple-reserved-39", "reserved", KeyEvent.VK_4, KeyEvent.META_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK | KeyEvent.CTRL_DOWN_MASK).setAutomatic(); // Capture a selection to the Clipboard.
    149                 Shortcut.registerSystemShortcut("apple-reserved-40", "reserved", KeyEvent.VK_8, KeyEvent.META_DOWN_MASK | KeyEvent.ALT_DOWN_MASK).setAutomatic(); // Turn screen zooming on or off. See Accessibility Overview.
    150                 Shortcut.registerSystemShortcut("apple-reserved-41", "reserved", KeyEvent.VK_8, KeyEvent.META_DOWN_MASK | KeyEvent.ALT_DOWN_MASK | KeyEvent.CTRL_DOWN_MASK).setAutomatic(); // Invert the screen colors. See Accessibility Overview.
    151 
    152                 Shortcut.registerSystemShortcut("system:selectall", "reserved", KeyEvent.VK_A, KeyEvent.META_DOWN_MASK); // Highlight every item in a document or window, or all characters in a text field (equivalent to the Select All command). See "The Edit Menu."
    153                 Shortcut.registerSystemShortcut("system:bold", "reserved", KeyEvent.VK_B, KeyEvent.META_DOWN_MASK); // Boldface the selected text or toggle boldfaced text on and off (equivalent to the Bold command). See "The Edit Menu."
    154                 Shortcut.registerSystemShortcut("system:copy", "reserved", KeyEvent.VK_C, KeyEvent.META_DOWN_MASK); // Duplicate the selected data and store on the Clipboard (equivalent to the Copy command). See "The Edit Menu."
    155                 Shortcut.registerSystemShortcut("system:colors", "reserved", KeyEvent.VK_C, KeyEvent.META_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK); // Display the Colors window (equivalent to the Show Colors command). See "The Format Menu."
    156                 Shortcut.registerSystemShortcut("system:copystyle", "reserved", KeyEvent.VK_C, KeyEvent.META_DOWN_MASK | KeyEvent.ALT_DOWN_MASK); // Copy the style of the selected text (equivalent to the Copy Style command). See "The Format Menu."
    157                 Shortcut.registerSystemShortcut("system:copyformat", "reserved", KeyEvent.VK_C, KeyEvent.META_DOWN_MASK | KeyEvent.CTRL_DOWN_MASK).setAutomatic(); // Copy the formatting settings of the selected item and store on the Clipboard (equivalent to the Copy Ruler command). See "The Format Menu."
    158 
    159                 Shortcut.registerSystemShortcut("apple-reserved-42", "reserved", KeyEvent.VK_D, KeyEvent.META_DOWN_MASK | KeyEvent.ALT_DOWN_MASK).setAutomatic(); // Show or hide the Dock. See "The Dock."
    160 
    161                 Shortcut.registerSystemShortcut("system:dictionarylookup", "reserved", KeyEvent.VK_D, KeyEvent.META_DOWN_MASK | KeyEvent.CTRL_DOWN_MASK); // Display the definition of the selected word in the Dictionary application.
    162                 Shortcut.registerSystemShortcut("system:findselected", "reserved", KeyEvent.VK_E, KeyEvent.META_DOWN_MASK); // Use the selection for a find operation. See "Find Windows."
    163                 Shortcut.registerSystemShortcut("system:find", "reserved", KeyEvent.VK_F, KeyEvent.META_DOWN_MASK); // Open a Find window (equivalent to the Find command). See "The Edit Menu."
    164                 Shortcut.registerSystemShortcut("system:search", "reserved", KeyEvent.VK_F, KeyEvent.META_DOWN_MASK | KeyEvent.ALT_DOWN_MASK); // Jump to the search field control. See "Search Fields."
    165                 Shortcut.registerSystemShortcut("system:findnext", "reserved", KeyEvent.VK_G, KeyEvent.META_DOWN_MASK); // Find the next occurrence of the selection (equivalent to the Find Next command). See "The Edit Menu."
    166                 Shortcut.registerSystemShortcut("system:findprev", "reserved", KeyEvent.VK_G, KeyEvent.META_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK); // Find the previous occurrence of the selection (equivalent to the Find Previous command). See "The Edit Menu."
    167                 Shortcut.registerSystemShortcut("system:hide", "reserved", KeyEvent.VK_H, KeyEvent.META_DOWN_MASK).setAutomatic(); // Hide the windows of the currently running application (equivalent to the Hide ApplicationName command). See "The Application Menu."
    168                 Shortcut.registerSystemShortcut("system:hideothers", "reserved", KeyEvent.VK_H, KeyEvent.META_DOWN_MASK | KeyEvent.ALT_DOWN_MASK).setAutomatic(); // Hide the windows of all other running applications (equivalent to the Hide Others command). See "The Application Menu."
    169                 // What about applications that have italic text AND info windows?
    170                 //Shortcut.registerSystemCut("system:italic", "reserved", KeyEvent.VK_I, KeyEvent.META_DOWN_MASK); // Italicize the selected text or toggle italic text on or off (equivalent to the Italic command). See "The Format Menu."
    171                 Shortcut.registerSystemShortcut("system:info", "reserved", KeyEvent.VK_I, KeyEvent.META_DOWN_MASK); // Display an Info window. See "Inspector Windows."
    172                 Shortcut.registerSystemShortcut("system:inspector", "reserved", KeyEvent.VK_I, KeyEvent.META_DOWN_MASK | KeyEvent.ALT_DOWN_MASK); // Display an inspector window. See "Inspector Windows."
    173                 Shortcut.registerSystemShortcut("system:toselection", "reserved", KeyEvent.VK_J, KeyEvent.META_DOWN_MASK); // Scroll to a selection.
    174                 Shortcut.registerSystemShortcut("system:minimize", "reserved", KeyEvent.VK_M, KeyEvent.META_DOWN_MASK); // Minimize the active window to the Dock (equivalent to the Minimize command). See "The Window Menu."
    175                 Shortcut.registerSystemShortcut("system:minimizeall", "reserved", KeyEvent.VK_M, KeyEvent.META_DOWN_MASK | KeyEvent.ALT_DOWN_MASK); // Minimize all windows of the active application to the Dock (equivalent to the Minimize All command). See "The Window Menu."
    176                 Shortcut.registerSystemShortcut("system:new", "reserved", KeyEvent.VK_N, KeyEvent.META_DOWN_MASK); // Open a new document (equivalent to the New command). See "The File Menu."
    177                 Shortcut.registerSystemShortcut("system:open", "reserved", KeyEvent.VK_O, KeyEvent.META_DOWN_MASK); // Display a dialog for choosing a document to open (equivalent to the Open command). See "The File Menu."
    178                 Shortcut.registerSystemShortcut("system:print", "reserved", KeyEvent.VK_P, KeyEvent.META_DOWN_MASK); // Display the Print dialog (equivalent to the Print command). See "The File Menu."
    179                 Shortcut.registerSystemShortcut("system:printsetup", "reserved", KeyEvent.VK_P, KeyEvent.META_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK); // Display a dialog for specifying printing parameters (equivalent to the Page Setup command). See "The File Menu."
    180                 Shortcut.registerSystemShortcut("system:menuexit", "reserved", KeyEvent.VK_Q, KeyEvent.META_DOWN_MASK).setAutomatic(); // Quit the application (equivalent to the Quit command). See "The Application Menu."
    181 
    182                 Shortcut.registerSystemShortcut("apple-reserved-43", "reserved", KeyEvent.VK_Q, KeyEvent.META_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK).setAutomatic(); // Log out the current user (equivalent to the Log Out command).
    183                 Shortcut.registerSystemShortcut("apple-reserved-44", "reserved", KeyEvent.VK_Q, KeyEvent.META_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK | KeyEvent.ALT_DOWN_MASK).setAutomatic(); // Log out the current user without confirmation.
    184 
    185                 Shortcut.registerSystemShortcut("system:save", "reserved", KeyEvent.VK_S, KeyEvent.META_DOWN_MASK); // Save the active document (equivalent to the Save command). See "The File Menu."
    186                 Shortcut.registerSystemShortcut("system:saveas", "reserved", KeyEvent.VK_S, KeyEvent.META_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK); // Display the Save dialog (equivalent to the Save As command). See "The File Menu."
    187                 Shortcut.registerSystemShortcut("system:fonts", "reserved", KeyEvent.VK_T, KeyEvent.META_DOWN_MASK); // Display the Fonts window (equivalent to the Show Fonts command). See "The Format Menu."
    188                 Shortcut.registerSystemShortcut("system:toggletoolbar", "reserved", KeyEvent.VK_T, KeyEvent.META_DOWN_MASK | KeyEvent.ALT_DOWN_MASK); // Show or hide a toolbar (equivalent to the Show/Hide Toolbar command). See "The View Menu" and "Toolbars."
    189                 Shortcut.registerSystemShortcut("system:underline", "reserved", KeyEvent.VK_U, KeyEvent.META_DOWN_MASK); // Underline the selected text or turn underlining on or off (equivalent to the Underline command). See "The Format Menu."
    190                 Shortcut.registerSystemShortcut("system:paste", "reserved", KeyEvent.VK_V, KeyEvent.META_DOWN_MASK); // Insert the Clipboard contents at the insertion point (equivalent to the Paste command). See "The File Menu."
    191                 Shortcut.registerSystemShortcut("system:pastestyle", "reserved", KeyEvent.VK_V, KeyEvent.META_DOWN_MASK | KeyEvent.ALT_DOWN_MASK); // Apply the style of one object to the selected object (equivalent to the Paste Style command). See "The Format Menu."
    192                 Shortcut.registerSystemShortcut("system:pastemwithoutstyle", "reserved", KeyEvent.VK_V, KeyEvent.META_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK | KeyEvent.ALT_DOWN_MASK); // Apply the style of the surrounding text to the inserted object (equivalent to the Paste and Match Style command). See "The Edit Menu."
    193                 Shortcut.registerSystemShortcut("system:pasteformatting", "reserved", KeyEvent.VK_V, KeyEvent.META_DOWN_MASK | KeyEvent.CTRL_DOWN_MASK); // Apply formatting settings to the selected object (equivalent to the Paste Ruler command). See "The Format Menu."
    194                 Shortcut.registerSystemShortcut("system:closewindow", "reserved", KeyEvent.VK_W, KeyEvent.META_DOWN_MASK); // Close the active window (equivalent to the Close command). See "The File Menu."
    195                 Shortcut.registerSystemShortcut("system:closefile", "reserved", KeyEvent.VK_W, KeyEvent.META_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK); // Close a file and its associated windows (equivalent to the Close File command). See "The File Menu."
    196                 Shortcut.registerSystemShortcut("system:closeallwindows", "reserved", KeyEvent.VK_W, KeyEvent.META_DOWN_MASK | KeyEvent.ALT_DOWN_MASK); // Close all windows in the application (equivalent to the Close All command). See "The File Menu."
    197                 Shortcut.registerSystemShortcut("system:cut", "reserved", KeyEvent.VK_X, KeyEvent.META_DOWN_MASK); // Remove the selection and store on the Clipboard (equivalent to the Cut command). See "The Edit Menu."
    198                 Shortcut.registerSystemShortcut("system:undo", "reserved", KeyEvent.VK_Z, KeyEvent.META_DOWN_MASK); // Reverse the effect of the user's previous operation (equivalent to the Undo command). See "The Edit Menu."
    199                 Shortcut.registerSystemShortcut("system:redo", "reserved", KeyEvent.VK_Z, KeyEvent.META_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK); // Reverse the effect of the last Undo command (equivalent to the Redo command). See "The Edit Menu."
    200 
    201                 Shortcut.registerSystemShortcut("apple-reserved-45", "reserved", KeyEvent.VK_RIGHT, KeyEvent.META_DOWN_MASK).setAutomatic(); // Change the keyboard layout to current layout of Roman script.
    202                 //Shortcut.registerSystemCut("apple-reserved-46", "reserved", KeyEvent.VK_RIGHT, KeyEvent.META_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK).setAutomatic(); // Extend selection to the next semantic unit, typically the end of the current line.
    203                 //Shortcut.registerSystemCut("apple-reserved-47", "reserved", KeyEvent.VK_RIGHT, KeyEvent.SHIFT_DOWN_MASK).setAutomatic(); // Extend selection one character to the right.
    204                 //Shortcut.registerSystemCut("apple-reserved-48", "reserved", KeyEvent.VK_RIGHT, KeyEvent.ALT_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK).setAutomatic(); // Extend selection to the end of the current word, then to the end of the next word.
    205 
    206                 Shortcut.registerSystemShortcut("system:movefocusright", "reserved", KeyEvent.VK_RIGHT, KeyEvent.CTRL_DOWN_MASK); // Move focus to another value or cell within a view, such as a table. See Accessibility Overview.
    207 
    208                 Shortcut.registerSystemShortcut("apple-reserved-49", "reserved", KeyEvent.VK_LEFT, KeyEvent.META_DOWN_MASK).setAutomatic(); // Change the keyboard layout to current layout of system script.
    209                 //Shortcut.registerSystemCut("apple-reserved-50", "reserved", KeyEvent.VK_LEFT, KeyEvent.META_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK).setAutomatic(); // Extend selection to the previous semantic unit, typically the beginning of the current line.
    210                 //Shortcut.registerSystemCut("apple-reserved-51", "reserved", KeyEvent.VK_LEFT, KeyEvent.SHIFT_DOWN_MASK).setAutomatic(); // Extend selection one character to the left.
    211                 //Shortcut.registerSystemCut("apple-reserved-52", "reserved", KeyEvent.VK_LEFT, KeyEvent.ALT_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK).setAutomatic(); // Extend selection to the beginning of the current word, then to the beginning of the previous word.
    212 
    213                 Shortcut.registerSystemShortcut("system:movefocusleft", "reserved", KeyEvent.VK_LEFT, KeyEvent.CTRL_DOWN_MASK); // Move focus to another value or cell within a view, such as a table. See Accessibility Overview.
    214 
    215                 //Shortcut.registerSystemCut("apple-reserved-53", "reserved", KeyEvent.VK_UP, KeyEvent.META_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK).setAutomatic(); // Extend selection upward in the next semantic unit, typically the beginning of the document.
    216                 //Shortcut.registerSystemCut("apple-reserved-54", "reserved", KeyEvent.VK_UP, KeyEvent.SHIFT_DOWN_MASK).setAutomatic(); // Extend selection to the line above, to the nearest character boundary at the same horizontal location.
    217                 //Shortcut.registerSystemCut("apple-reserved-55", "reserved", KeyEvent.VK_UP, KeyEvent.ALT_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK).setAutomatic(); // Extend selection to the beginning of the current paragraph, then to the beginning of the next paragraph.
    218 
    219                 Shortcut.registerSystemShortcut("system:movefocusup", "reserved", KeyEvent.VK_UP, KeyEvent.CTRL_DOWN_MASK); // Move focus to another value or cell within a view, such as a table. See Accessibility Overview.
    220 
    221                 //Shortcut.registerSystemCut("apple-reserved-56", "reserved", KeyEvent.VK_DOWN, KeyEvent.META_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK).setAutomatic(); // Extend selection downward in the next semantic unit, typically the end of the document.
    222                 //Shortcut.registerSystemCut("apple-reserved-57", "reserved", KeyEvent.VK_DOWN, KeyEvent.SHIFT_DOWN_MASK).setAutomatic(); // Extend selection to the line below, to the nearest character boundary at the same horizontal location.
    223                 //Shortcut.registerSystemCut("apple-reserved-58", "reserved", KeyEvent.VK_DOWN, KeyEvent.ALT_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK).setAutomatic(); // Extend selection to the end of the current paragraph, then to the end of the next paragraph (include the blank line between paragraphs in cut, copy, and paste operations).
    224 
    225                 Shortcut.registerSystemShortcut("system:movefocusdown", "reserved", KeyEvent.VK_DOWN, KeyEvent.CTRL_DOWN_MASK); // Move focus to another value or cell within a view, such as a table. See Accessibility Overview.
    226 
    227                 Shortcut.registerSystemShortcut("system:about", "reserved", 0, -1).setAutomatic(); // About
    228         }
    229         public String makeTooltip(String name, Shortcut sc) {
    230                 String lafid = UIManager.getLookAndFeel().getID();
    231                 boolean canHtml = true;
    232                 // "Mac" is the native LAF, "Aqua" is Quaqua. Both use native menus with native tooltips.
    233                 if (lafid.contains("Mac") || lafid.contains("Aqua")) {
    234                         canHtml = false;
    235                 }
    236                 String result = "";
    237                 if (canHtml) result += "<html>";
    238                 result += name;
    239                 if (sc != null && sc.getKeyText().length() != 0) {
    240                         result += " ";
    241                         if (canHtml) result += "<font size='-2'>";
    242                         result += "("+sc.getKeyText()+")";
    243                         if (canHtml) result += "</font>";
    244                 }
    245                 if (canHtml) result += "&nbsp;</html>";
    246                 return result;
    247         }
     18    private static PlatformHookOsx ivhandler = new PlatformHookOsx();
     19    public void preStartupHook(){
     20        // This will merge our MenuBar into the system menu.
     21        // MUST be set before Swing is initialized!
     22        // And will not work when one of the system independet LAFs is used.
     23        // They just insist on painting themselves...
     24        System.setProperty("apple.laf.useScreenMenuBar", "true");
     25    }
     26    public void startupHook() {
     27        // Here we register callbacks for the menu entries in the system menu
     28        try {
     29            Class Ccom_apple_eawt_Application = Class.forName("com.apple.eawt.Application");
     30            Object Ocom_apple_eawt_Application = Ccom_apple_eawt_Application.getConstructor((Class[])null).newInstance((Object[])null);
     31            Class Ccom_apple_eawt_ApplicationListener = Class.forName("com.apple.eawt.ApplicationListener");
     32            Method MaddApplicationListener = Ccom_apple_eawt_Application.getDeclaredMethod("addApplicationListener", new Class[] { Ccom_apple_eawt_ApplicationListener });
     33            Object Oproxy = Proxy.newProxyInstance(PlatformHookOsx.class.getClassLoader(), new Class[] { Ccom_apple_eawt_ApplicationListener }, ivhandler);
     34            MaddApplicationListener.invoke(Ocom_apple_eawt_Application, new Object[] { Oproxy });
     35            Method MsetEnabledPreferencesMenu = Ccom_apple_eawt_Application.getDeclaredMethod("setEnabledPreferencesMenu", new Class[] { boolean.class });
     36            MsetEnabledPreferencesMenu.invoke(Ocom_apple_eawt_Application, new Object[] { Boolean.TRUE });
     37        } catch (Exception ex) {
     38            // Oops, what now?
     39            // We'll just ignore this for now. The user will still be able to close JOSM
     40            // by closing all its windows.
     41            System.out.println("Failed to register with OSX: " + ex);
     42        }
     43    }
     44    public Object invoke (Object proxy, Method method, Object[] args) throws Throwable {
     45        Boolean handled = Boolean.TRUE;
     46        //System.out.println("Going to handle method "+method+" (short: "+method.getName()+") with event "+args[0]);
     47        if (method.getName().equals("handleQuit")) {
     48            handled = !Main.main.breakBecauseUnsavedChanges();
     49        } else if (method.getName().equals("handleAbout")) {
     50            Main.main.menu.about.actionPerformed(null);
     51        } else if (method.getName().equals("handlePreferences")) {
     52            Main.main.menu.preferences.actionPerformed(null);
     53        } else {
     54            return null;
     55        }
     56        if (args[0] != null) {
     57            try {
     58                args[0].getClass().getDeclaredMethod("setHandled", new Class[] { boolean.class }).invoke(args[0], new Object[] { handled });
     59            } catch (Exception ex) {
     60                System.out.println("Failed to report handled event: " + ex);
     61            }
     62        }
     63        return null;
     64    }
     65    public void openUrl(String url) throws IOException {
     66        // Ain't that KISS?
     67        Runtime.getRuntime().exec("open " + url);
     68    }
     69    public void initShortcutGroups() {
     70        // Everything but Shortcut.GROUPS_DEFAULT+Shortcut.GROUP_MENU is guesswork.
     71        Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_DEFAULT+Shortcut.GROUP_NONE),    Integer.toString(-1));
     72        Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_DEFAULT+Shortcut.GROUP_HOTKEY),  Integer.toString(KeyEvent.CTRL_DOWN_MASK));
     73        Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_DEFAULT+Shortcut.GROUP_MENU),    Integer.toString(KeyEvent.META_DOWN_MASK));
     74        Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_DEFAULT+Shortcut.GROUP_EDIT),    Integer.toString(0));
     75        Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_DEFAULT+Shortcut.GROUP_LAYER),   Integer.toString(KeyEvent.ALT_DOWN_MASK));
     76        Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_DEFAULT+Shortcut.GROUP_DIRECT),  Integer.toString(0));
     77        Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_DEFAULT+Shortcut.GROUP_MNEMONIC),Integer.toString(KeyEvent.ALT_DOWN_MASK));
     78
     79        Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT1+Shortcut.GROUP_NONE),       Integer.toString(-1));
     80        Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT1+Shortcut.GROUP_HOTKEY),     Integer.toString(KeyEvent.CTRL_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK));
     81        Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT1+Shortcut.GROUP_MENU),       Integer.toString(KeyEvent.META_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK));
     82        Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT1+Shortcut.GROUP_EDIT),       Integer.toString(KeyEvent.SHIFT_DOWN_MASK));
     83        Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT1+Shortcut.GROUP_LAYER),      Integer.toString(KeyEvent.ALT_DOWN_MASK  | KeyEvent.SHIFT_DOWN_MASK));
     84        Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT1+Shortcut.GROUP_DIRECT),     Integer.toString(KeyEvent.SHIFT_DOWN_MASK));
     85        Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT1+Shortcut.GROUP_MNEMONIC),   Integer.toString(KeyEvent.ALT_DOWN_MASK));
     86
     87        Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT2+Shortcut.GROUP_NONE),       Integer.toString(-1));
     88        Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT2+Shortcut.GROUP_HOTKEY),     Integer.toString(KeyEvent.CTRL_DOWN_MASK | KeyEvent.ALT_DOWN_MASK));
     89        Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT2+Shortcut.GROUP_MENU),       Integer.toString(KeyEvent.META_DOWN_MASK | KeyEvent.ALT_DOWN_MASK));
     90        Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT2+Shortcut.GROUP_EDIT),       Integer.toString(KeyEvent.ALT_DOWN_MASK  | KeyEvent.SHIFT_DOWN_MASK));
     91        Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT2+Shortcut.GROUP_LAYER),      Integer.toString(KeyEvent.ALT_DOWN_MASK));
     92        Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT2+Shortcut.GROUP_DIRECT),     Integer.toString(KeyEvent.SHIFT_DOWN_MASK | KeyEvent.ALT_DOWN_MASK));
     93        Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT2+Shortcut.GROUP_MNEMONIC),   Integer.toString(KeyEvent.ALT_DOWN_MASK));
     94    }
     95    public void initSystemShortcuts() {
     96        // Yeah, it's a long, long list. And people always complain that OSX has no shortcuts.
     97        Shortcut.registerSystemShortcut("apple-reserved-01", "reserved", KeyEvent.VK_SPACE, KeyEvent.META_DOWN_MASK).setAutomatic(); // Show or hide the Spotlight search field (when multiple languages are installed, may rotate through enabled script systems).
     98        Shortcut.registerSystemShortcut("apple-reserved-02", "reserved", KeyEvent.VK_SPACE, KeyEvent.META_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK).setAutomatic(); // Apple reserved.
     99        Shortcut.registerSystemShortcut("apple-reserved-03", "reserved", KeyEvent.VK_SPACE, KeyEvent.META_DOWN_MASK | KeyEvent.ALT_DOWN_MASK).setAutomatic(); // Show the Spotlight search results window (when multiple languages are installed, may rotate through keyboard layouts and input methods within a script).
     100        Shortcut.registerSystemShortcut("apple-reserved-04", "reserved", KeyEvent.VK_SPACE, KeyEvent.META_DOWN_MASK | KeyEvent.CTRL_DOWN_MASK).setAutomatic(); //  | Apple reserved.
     101        Shortcut.registerSystemShortcut("apple-reserved-05", "reserved", KeyEvent.VK_TAB, KeyEvent.SHIFT_DOWN_MASK).setAutomatic(); // Navigate through controls in a reverse direction. See "Keyboard Focus and Navigation."
     102        Shortcut.registerSystemShortcut("apple-reserved-06", "reserved", KeyEvent.VK_TAB, KeyEvent.META_DOWN_MASK).setAutomatic(); // Move forward to the next most recently used application in a list of open applications.
     103        Shortcut.registerSystemShortcut("apple-reserved-07", "reserved", KeyEvent.VK_TAB, KeyEvent.META_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK).setAutomatic(); // Move backward through a list of open applications (sorted by recent use).
     104        Shortcut.registerSystemShortcut("apple-reserved-08", "reserved", KeyEvent.VK_TAB, KeyEvent.CTRL_DOWN_MASK).setAutomatic(); // Move focus to the next grouping of controls in a dialog or the next table (when Tab moves to the next cell). See Accessibility Overview.
     105        Shortcut.registerSystemShortcut("apple-reserved-09", "reserved", KeyEvent.VK_TAB, KeyEvent.CTRL_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK).setAutomatic(); // Move focus to the previous grouping of controls. See Accessibility Overview.
     106        Shortcut.registerSystemShortcut("apple-reserved-10", "reserved", KeyEvent.VK_ESCAPE, KeyEvent.META_DOWN_MASK).setAutomatic(); // Open Front Row.
     107        Shortcut.registerSystemShortcut("apple-reserved-11", "reserved", KeyEvent.VK_ESCAPE, KeyEvent.META_DOWN_MASK | KeyEvent.ALT_DOWN_MASK).setAutomatic(); // Open the Force Quit dialog.
     108        Shortcut.registerSystemShortcut("apple-reserved-12", "reserved", KeyEvent.VK_F1, KeyEvent.CTRL_DOWN_MASK).setAutomatic(); // Toggle full keyboard access on or off. See Accessibility Overview.
     109        Shortcut.registerSystemShortcut("apple-reserved-13", "reserved", KeyEvent.VK_F2, KeyEvent.CTRL_DOWN_MASK).setAutomatic(); // Move focus to the menu bar. See Accessibility Overview.
     110        Shortcut.registerSystemShortcut("apple-reserved-14", "reserved", KeyEvent.VK_F3, KeyEvent.CTRL_DOWN_MASK).setAutomatic(); // Move focus to the Dock. See Accessibility Overview.
     111        Shortcut.registerSystemShortcut("apple-reserved-15", "reserved", KeyEvent.VK_F4, KeyEvent.CTRL_DOWN_MASK).setAutomatic(); // Move focus to the active (or next) window. See Accessibility Overview.
     112        Shortcut.registerSystemShortcut("apple-reserved-16", "reserved", KeyEvent.VK_F4, KeyEvent.CTRL_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK).setAutomatic(); // Move focus to the previously active window. See Accessibility Overview.
     113        Shortcut.registerSystemShortcut("apple-reserved-17", "reserved", KeyEvent.VK_F5, KeyEvent.CTRL_DOWN_MASK).setAutomatic(); // Move focus to the toolbar. See Accessibility Overview.
     114        Shortcut.registerSystemShortcut("apple-reserved-18", "reserved", KeyEvent.VK_F5, KeyEvent.META_DOWN_MASK).setAutomatic(); // Turn VoiceOver on or off. See Accessibility Overview.
     115        Shortcut.registerSystemShortcut("apple-reserved-19", "reserved", KeyEvent.VK_F6, KeyEvent.CTRL_DOWN_MASK).setAutomatic(); // Move focus to the first (or next) panel. See Accessibility Overview.
     116        Shortcut.registerSystemShortcut("apple-reserved-20", "reserved", KeyEvent.VK_F6, KeyEvent.CTRL_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK).setAutomatic(); // Move focus to the previous panel. See Accessibility Overview.
     117        Shortcut.registerSystemShortcut("apple-reserved-21", "reserved", KeyEvent.VK_F7, KeyEvent.CTRL_DOWN_MASK).setAutomatic(); // Temporarily override the current keyboard access mode in windows and dialogs. See Accessibility Overview.
     118        Shortcut.registerSystemShortcut("apple-reserved-22", "reserved", KeyEvent.VK_F9, 0).setAutomatic(); // Tile or untile all open windows.
     119        Shortcut.registerSystemShortcut("apple-reserved-23", "reserved", KeyEvent.VK_F10, 0).setAutomatic(); // Tile or untile all open windows in the currently active application.
     120        Shortcut.registerSystemShortcut("apple-reserved-24", "reserved", KeyEvent.VK_F11, 0).setAutomatic(); // Hide or show all open windows.
     121        Shortcut.registerSystemShortcut("apple-reserved-25", "reserved", KeyEvent.VK_F12, 0).setAutomatic(); // Hide or display Dashboard.
     122        Shortcut.registerSystemShortcut("apple-reserved-26", "reserved", KeyEvent.VK_DEAD_GRAVE, KeyEvent.META_DOWN_MASK).setAutomatic(); // Activate the next open window in the frontmost application. See "Window Layering."
     123        Shortcut.registerSystemShortcut("apple-reserved-27", "reserved", KeyEvent.VK_DEAD_GRAVE, KeyEvent.META_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK).setAutomatic(); // Activate the previous open window in the frontmost application. See "Window Layering."
     124        Shortcut.registerSystemShortcut("apple-reserved-28", "reserved", KeyEvent.VK_DEAD_GRAVE, KeyEvent.META_DOWN_MASK | KeyEvent.ALT_DOWN_MASK).setAutomatic(); // Move focus to the window drawer.
     125        Shortcut.registerSystemShortcut("apple-reserved-29", "reserved", KeyEvent.VK_MINUS, KeyEvent.META_DOWN_MASK).setAutomatic(); // Decrease the size of the selected item (equivalent to the Smaller command). See "The Format Menu."
     126        Shortcut.registerSystemShortcut("apple-reserved-30", "reserved", KeyEvent.VK_MINUS, KeyEvent.META_DOWN_MASK | KeyEvent.ALT_DOWN_MASK).setAutomatic(); // Zoom out when screen zooming is on. See Accessibility Overview.
     127
     128        Shortcut.registerSystemShortcut("system:align-left", "reserved", KeyEvent.VK_OPEN_BRACKET, KeyEvent.META_DOWN_MASK); // Left-align a selection (equivalent to the Align Left command). See "The Format Menu."
     129        Shortcut.registerSystemShortcut("system:align-right","reserved", KeyEvent.VK_CLOSE_BRACKET, KeyEvent.META_DOWN_MASK); // Right-align a selection (equivalent to the Align Right command). See "The Format Menu."
     130        // I found no KeyEvent for |
     131        //Shortcut.registerSystemCut("system:align-center", "reserved", '|', KeyEvent.META_DOWN_MASK); // Center-align a selection (equivalent to the Align Center command). See "The Format Menu."
     132        Shortcut.registerSystemShortcut("system:spelling", "reserved", KeyEvent.VK_COLON, KeyEvent.META_DOWN_MASK); // Display the Spelling window (equivalent to the Spelling command). See "The Edit Menu."
     133        Shortcut.registerSystemShortcut("system:spellcheck", "reserved", KeyEvent.VK_SEMICOLON, KeyEvent.META_DOWN_MASK); // Find misspelled words in the document (equivalent to the Check Spelling command). See "The Edit Menu."
     134        Shortcut.registerSystemShortcut("system:preferences", "reserved", KeyEvent.VK_COMMA, KeyEvent.META_DOWN_MASK).setAutomatic(); // Open the application's preferences window (equivalent to the Preferences command). See "The Application Menu."
     135
     136        Shortcut.registerSystemShortcut("apple-reserved-31", "reserved", KeyEvent.VK_COMMA, KeyEvent.META_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK | KeyEvent.ALT_DOWN_MASK).setAutomatic(); // Decrease screen contrast. See Accessibility Overview.
     137        Shortcut.registerSystemShortcut("apple-reserved-32", "reserved", KeyEvent.VK_PERIOD, KeyEvent.META_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK | KeyEvent.ALT_DOWN_MASK).setAutomatic(); // Increase screen contrast. See Accessibility Overview.
     138
     139        // I found no KeyEvent for ?
     140        //Shortcut.registerSystemCut("system:help", "reserved", '?', KeyEvent.META_DOWN_MASK).setAutomatic(); // Open the application's help in Help Viewer. See "The Help Menu."
     141
     142        Shortcut.registerSystemShortcut("apple-reserved-33", "reserved", KeyEvent.VK_SLASH, KeyEvent.META_DOWN_MASK | KeyEvent.ALT_DOWN_MASK).setAutomatic(); // Turn font smoothing on or off.
     143        Shortcut.registerSystemShortcut("apple-reserved-34", "reserved", KeyEvent.VK_EQUALS, KeyEvent.META_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK).setAutomatic(); // Increase the size of the selected item (equivalent to the Bigger command). See "The Format Menu."
     144        Shortcut.registerSystemShortcut("apple-reserved-35", "reserved", KeyEvent.VK_EQUALS, KeyEvent.META_DOWN_MASK | KeyEvent.ALT_DOWN_MASK).setAutomatic(); // Zoom in when screen zooming is on. See Accessibility Overview.
     145        Shortcut.registerSystemShortcut("apple-reserved-36", "reserved", KeyEvent.VK_3, KeyEvent.META_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK).setAutomatic(); // Capture the screen to a file.
     146        Shortcut.registerSystemShortcut("apple-reserved-37", "reserved", KeyEvent.VK_3, KeyEvent.META_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK | KeyEvent.CTRL_DOWN_MASK).setAutomatic(); // Capture the screen to the Clipboard.
     147        Shortcut.registerSystemShortcut("apple-reserved-38", "reserved", KeyEvent.VK_4, KeyEvent.META_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK).setAutomatic(); // Capture a selection to a file.
     148        Shortcut.registerSystemShortcut("apple-reserved-39", "reserved", KeyEvent.VK_4, KeyEvent.META_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK | KeyEvent.CTRL_DOWN_MASK).setAutomatic(); // Capture a selection to the Clipboard.
     149        Shortcut.registerSystemShortcut("apple-reserved-40", "reserved", KeyEvent.VK_8, KeyEvent.META_DOWN_MASK | KeyEvent.ALT_DOWN_MASK).setAutomatic(); // Turn screen zooming on or off. See Accessibility Overview.
     150        Shortcut.registerSystemShortcut("apple-reserved-41", "reserved", KeyEvent.VK_8, KeyEvent.META_DOWN_MASK | KeyEvent.ALT_DOWN_MASK | KeyEvent.CTRL_DOWN_MASK).setAutomatic(); // Invert the screen colors. See Accessibility Overview.
     151
     152        Shortcut.registerSystemShortcut("system:selectall", "reserved", KeyEvent.VK_A, KeyEvent.META_DOWN_MASK); // Highlight every item in a document or window, or all characters in a text field (equivalent to the Select All command). See "The Edit Menu."
     153        Shortcut.registerSystemShortcut("system:bold", "reserved", KeyEvent.VK_B, KeyEvent.META_DOWN_MASK); // Boldface the selected text or toggle boldfaced text on and off (equivalent to the Bold command). See "The Edit Menu."
     154        Shortcut.registerSystemShortcut("system:copy", "reserved", KeyEvent.VK_C, KeyEvent.META_DOWN_MASK); // Duplicate the selected data and store on the Clipboard (equivalent to the Copy command). See "The Edit Menu."
     155        Shortcut.registerSystemShortcut("system:colors", "reserved", KeyEvent.VK_C, KeyEvent.META_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK); // Display the Colors window (equivalent to the Show Colors command). See "The Format Menu."
     156        Shortcut.registerSystemShortcut("system:copystyle", "reserved", KeyEvent.VK_C, KeyEvent.META_DOWN_MASK | KeyEvent.ALT_DOWN_MASK); // Copy the style of the selected text (equivalent to the Copy Style command). See "The Format Menu."
     157        Shortcut.registerSystemShortcut("system:copyformat", "reserved", KeyEvent.VK_C, KeyEvent.META_DOWN_MASK | KeyEvent.CTRL_DOWN_MASK).setAutomatic(); // Copy the formatting settings of the selected item and store on the Clipboard (equivalent to the Copy Ruler command). See "The Format Menu."
     158
     159        Shortcut.registerSystemShortcut("apple-reserved-42", "reserved", KeyEvent.VK_D, KeyEvent.META_DOWN_MASK | KeyEvent.ALT_DOWN_MASK).setAutomatic(); // Show or hide the Dock. See "The Dock."
     160
     161        Shortcut.registerSystemShortcut("system:dictionarylookup", "reserved", KeyEvent.VK_D, KeyEvent.META_DOWN_MASK | KeyEvent.CTRL_DOWN_MASK); // Display the definition of the selected word in the Dictionary application.
     162        Shortcut.registerSystemShortcut("system:findselected", "reserved", KeyEvent.VK_E, KeyEvent.META_DOWN_MASK); // Use the selection for a find operation. See "Find Windows."
     163        Shortcut.registerSystemShortcut("system:find", "reserved", KeyEvent.VK_F, KeyEvent.META_DOWN_MASK); // Open a Find window (equivalent to the Find command). See "The Edit Menu."
     164        Shortcut.registerSystemShortcut("system:search", "reserved", KeyEvent.VK_F, KeyEvent.META_DOWN_MASK | KeyEvent.ALT_DOWN_MASK); // Jump to the search field control. See "Search Fields."
     165        Shortcut.registerSystemShortcut("system:findnext", "reserved", KeyEvent.VK_G, KeyEvent.META_DOWN_MASK); // Find the next occurrence of the selection (equivalent to the Find Next command). See "The Edit Menu."
     166        Shortcut.registerSystemShortcut("system:findprev", "reserved", KeyEvent.VK_G, KeyEvent.META_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK); // Find the previous occurrence of the selection (equivalent to the Find Previous command). See "The Edit Menu."
     167        Shortcut.registerSystemShortcut("system:hide", "reserved", KeyEvent.VK_H, KeyEvent.META_DOWN_MASK).setAutomatic(); // Hide the windows of the currently running application (equivalent to the Hide ApplicationName command). See "The Application Menu."
     168        Shortcut.registerSystemShortcut("system:hideothers", "reserved", KeyEvent.VK_H, KeyEvent.META_DOWN_MASK | KeyEvent.ALT_DOWN_MASK).setAutomatic(); // Hide the windows of all other running applications (equivalent to the Hide Others command). See "The Application Menu."
     169        // What about applications that have italic text AND info windows?
     170        //Shortcut.registerSystemCut("system:italic", "reserved", KeyEvent.VK_I, KeyEvent.META_DOWN_MASK); // Italicize the selected text or toggle italic text on or off (equivalent to the Italic command). See "The Format Menu."
     171        Shortcut.registerSystemShortcut("system:info", "reserved", KeyEvent.VK_I, KeyEvent.META_DOWN_MASK); // Display an Info window. See "Inspector Windows."
     172        Shortcut.registerSystemShortcut("system:inspector", "reserved", KeyEvent.VK_I, KeyEvent.META_DOWN_MASK | KeyEvent.ALT_DOWN_MASK); // Display an inspector window. See "Inspector Windows."
     173        Shortcut.registerSystemShortcut("system:toselection", "reserved", KeyEvent.VK_J, KeyEvent.META_DOWN_MASK); // Scroll to a selection.
     174        Shortcut.registerSystemShortcut("system:minimize", "reserved", KeyEvent.VK_M, KeyEvent.META_DOWN_MASK); // Minimize the active window to the Dock (equivalent to the Minimize command). See "The Window Menu."
     175        Shortcut.registerSystemShortcut("system:minimizeall", "reserved", KeyEvent.VK_M, KeyEvent.META_DOWN_MASK | KeyEvent.ALT_DOWN_MASK); // Minimize all windows of the active application to the Dock (equivalent to the Minimize All command). See "The Window Menu."
     176        Shortcut.registerSystemShortcut("system:new", "reserved", KeyEvent.VK_N, KeyEvent.META_DOWN_MASK); // Open a new document (equivalent to the New command). See "The File Menu."
     177        Shortcut.registerSystemShortcut("system:open", "reserved", KeyEvent.VK_O, KeyEvent.META_DOWN_MASK); // Display a dialog for choosing a document to open (equivalent to the Open command). See "The File Menu."
     178        Shortcut.registerSystemShortcut("system:print", "reserved", KeyEvent.VK_P, KeyEvent.META_DOWN_MASK); // Display the Print dialog (equivalent to the Print command). See "The File Menu."
     179        Shortcut.registerSystemShortcut("system:printsetup", "reserved", KeyEvent.VK_P, KeyEvent.META_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK); // Display a dialog for specifying printing parameters (equivalent to the Page Setup command). See "The File Menu."
     180        Shortcut.registerSystemShortcut("system:menuexit", "reserved", KeyEvent.VK_Q, KeyEvent.META_DOWN_MASK).setAutomatic(); // Quit the application (equivalent to the Quit command). See "The Application Menu."
     181
     182        Shortcut.registerSystemShortcut("apple-reserved-43", "reserved", KeyEvent.VK_Q, KeyEvent.META_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK).setAutomatic(); // Log out the current user (equivalent to the Log Out command).
     183        Shortcut.registerSystemShortcut("apple-reserved-44", "reserved", KeyEvent.VK_Q, KeyEvent.META_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK | KeyEvent.ALT_DOWN_MASK).setAutomatic(); // Log out the current user without confirmation.
     184
     185        Shortcut.registerSystemShortcut("system:save", "reserved", KeyEvent.VK_S, KeyEvent.META_DOWN_MASK); // Save the active document (equivalent to the Save command). See "The File Menu."
     186        Shortcut.registerSystemShortcut("system:saveas", "reserved", KeyEvent.VK_S, KeyEvent.META_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK); // Display the Save dialog (equivalent to the Save As command). See "The File Menu."
     187        Shortcut.registerSystemShortcut("system:fonts", "reserved", KeyEvent.VK_T, KeyEvent.META_DOWN_MASK); // Display the Fonts window (equivalent to the Show Fonts command). See "The Format Menu."
     188        Shortcut.registerSystemShortcut("system:toggletoolbar", "reserved", KeyEvent.VK_T, KeyEvent.META_DOWN_MASK | KeyEvent.ALT_DOWN_MASK); // Show or hide a toolbar (equivalent to the Show/Hide Toolbar command). See "The View Menu" and "Toolbars."
     189        Shortcut.registerSystemShortcut("system:underline", "reserved", KeyEvent.VK_U, KeyEvent.META_DOWN_MASK); // Underline the selected text or turn underlining on or off (equivalent to the Underline command). See "The Format Menu."
     190        Shortcut.registerSystemShortcut("system:paste", "reserved", KeyEvent.VK_V, KeyEvent.META_DOWN_MASK); // Insert the Clipboard contents at the insertion point (equivalent to the Paste command). See "The File Menu."
     191        Shortcut.registerSystemShortcut("system:pastestyle", "reserved", KeyEvent.VK_V, KeyEvent.META_DOWN_MASK | KeyEvent.ALT_DOWN_MASK); // Apply the style of one object to the selected object (equivalent to the Paste Style command). See "The Format Menu."
     192        Shortcut.registerSystemShortcut("system:pastemwithoutstyle", "reserved", KeyEvent.VK_V, KeyEvent.META_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK | KeyEvent.ALT_DOWN_MASK); // Apply the style of the surrounding text to the inserted object (equivalent to the Paste and Match Style command). See "The Edit Menu."
     193        Shortcut.registerSystemShortcut("system:pasteformatting", "reserved", KeyEvent.VK_V, KeyEvent.META_DOWN_MASK | KeyEvent.CTRL_DOWN_MASK); // Apply formatting settings to the selected object (equivalent to the Paste Ruler command). See "The Format Menu."
     194        Shortcut.registerSystemShortcut("system:closewindow", "reserved", KeyEvent.VK_W, KeyEvent.META_DOWN_MASK); // Close the active window (equivalent to the Close command). See "The File Menu."
     195        Shortcut.registerSystemShortcut("system:closefile", "reserved", KeyEvent.VK_W, KeyEvent.META_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK); // Close a file and its associated windows (equivalent to the Close File command). See "The File Menu."
     196        Shortcut.registerSystemShortcut("system:closeallwindows", "reserved", KeyEvent.VK_W, KeyEvent.META_DOWN_MASK | KeyEvent.ALT_DOWN_MASK); // Close all windows in the application (equivalent to the Close All command). See "The File Menu."
     197        Shortcut.registerSystemShortcut("system:cut", "reserved", KeyEvent.VK_X, KeyEvent.META_DOWN_MASK); // Remove the selection and store on the Clipboard (equivalent to the Cut command). See "The Edit Menu."
     198        Shortcut.registerSystemShortcut("system:undo", "reserved", KeyEvent.VK_Z, KeyEvent.META_DOWN_MASK); // Reverse the effect of the user's previous operation (equivalent to the Undo command). See "The Edit Menu."
     199        Shortcut.registerSystemShortcut("system:redo", "reserved", KeyEvent.VK_Z, KeyEvent.META_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK); // Reverse the effect of the last Undo command (equivalent to the Redo command). See "The Edit Menu."
     200
     201        Shortcut.registerSystemShortcut("apple-reserved-45", "reserved", KeyEvent.VK_RIGHT, KeyEvent.META_DOWN_MASK).setAutomatic(); // Change the keyboard layout to current layout of Roman script.
     202        //Shortcut.registerSystemCut("apple-reserved-46", "reserved", KeyEvent.VK_RIGHT, KeyEvent.META_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK).setAutomatic(); // Extend selection to the next semantic unit, typically the end of the current line.
     203        //Shortcut.registerSystemCut("apple-reserved-47", "reserved", KeyEvent.VK_RIGHT, KeyEvent.SHIFT_DOWN_MASK).setAutomatic(); // Extend selection one character to the right.
     204        //Shortcut.registerSystemCut("apple-reserved-48", "reserved", KeyEvent.VK_RIGHT, KeyEvent.ALT_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK).setAutomatic(); // Extend selection to the end of the current word, then to the end of the next word.
     205
     206        Shortcut.registerSystemShortcut("system:movefocusright", "reserved", KeyEvent.VK_RIGHT, KeyEvent.CTRL_DOWN_MASK); // Move focus to another value or cell within a view, such as a table. See Accessibility Overview.
     207
     208        Shortcut.registerSystemShortcut("apple-reserved-49", "reserved", KeyEvent.VK_LEFT, KeyEvent.META_DOWN_MASK).setAutomatic(); // Change the keyboard layout to current layout of system script.
     209        //Shortcut.registerSystemCut("apple-reserved-50", "reserved", KeyEvent.VK_LEFT, KeyEvent.META_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK).setAutomatic(); // Extend selection to the previous semantic unit, typically the beginning of the current line.
     210        //Shortcut.registerSystemCut("apple-reserved-51", "reserved", KeyEvent.VK_LEFT, KeyEvent.SHIFT_DOWN_MASK).setAutomatic(); // Extend selection one character to the left.
     211        //Shortcut.registerSystemCut("apple-reserved-52", "reserved", KeyEvent.VK_LEFT, KeyEvent.ALT_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK).setAutomatic(); // Extend selection to the beginning of the current word, then to the beginning of the previous word.
     212
     213        Shortcut.registerSystemShortcut("system:movefocusleft", "reserved", KeyEvent.VK_LEFT, KeyEvent.CTRL_DOWN_MASK); // Move focus to another value or cell within a view, such as a table. See Accessibility Overview.
     214
     215        //Shortcut.registerSystemCut("apple-reserved-53", "reserved", KeyEvent.VK_UP, KeyEvent.META_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK).setAutomatic(); // Extend selection upward in the next semantic unit, typically the beginning of the document.
     216        //Shortcut.registerSystemCut("apple-reserved-54", "reserved", KeyEvent.VK_UP, KeyEvent.SHIFT_DOWN_MASK).setAutomatic(); // Extend selection to the line above, to the nearest character boundary at the same horizontal location.
     217        //Shortcut.registerSystemCut("apple-reserved-55", "reserved", KeyEvent.VK_UP, KeyEvent.ALT_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK).setAutomatic(); // Extend selection to the beginning of the current paragraph, then to the beginning of the next paragraph.
     218
     219        Shortcut.registerSystemShortcut("system:movefocusup", "reserved", KeyEvent.VK_UP, KeyEvent.CTRL_DOWN_MASK); // Move focus to another value or cell within a view, such as a table. See Accessibility Overview.
     220
     221        //Shortcut.registerSystemCut("apple-reserved-56", "reserved", KeyEvent.VK_DOWN, KeyEvent.META_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK).setAutomatic(); // Extend selection downward in the next semantic unit, typically the end of the document.
     222        //Shortcut.registerSystemCut("apple-reserved-57", "reserved", KeyEvent.VK_DOWN, KeyEvent.SHIFT_DOWN_MASK).setAutomatic(); // Extend selection to the line below, to the nearest character boundary at the same horizontal location.
     223        //Shortcut.registerSystemCut("apple-reserved-58", "reserved", KeyEvent.VK_DOWN, KeyEvent.ALT_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK).setAutomatic(); // Extend selection to the end of the current paragraph, then to the end of the next paragraph (include the blank line between paragraphs in cut, copy, and paste operations).
     224
     225        Shortcut.registerSystemShortcut("system:movefocusdown", "reserved", KeyEvent.VK_DOWN, KeyEvent.CTRL_DOWN_MASK); // Move focus to another value or cell within a view, such as a table. See Accessibility Overview.
     226
     227        Shortcut.registerSystemShortcut("system:about", "reserved", 0, -1).setAutomatic(); // About
     228    }
     229    public String makeTooltip(String name, Shortcut sc) {
     230        String lafid = UIManager.getLookAndFeel().getID();
     231        boolean canHtml = true;
     232        // "Mac" is the native LAF, "Aqua" is Quaqua. Both use native menus with native tooltips.
     233        if (lafid.contains("Mac") || lafid.contains("Aqua")) {
     234            canHtml = false;
     235        }
     236        String result = "";
     237        if (canHtml) result += "<html>";
     238        result += name;
     239        if (sc != null && sc.getKeyText().length() != 0) {
     240            result += " ";
     241            if (canHtml) result += "<font size='-2'>";
     242            result += "("+sc.getKeyText()+")";
     243            if (canHtml) result += "</font>";
     244        }
     245        if (canHtml) result += "&nbsp;</html>";
     246        return result;
     247    }
    248248}
  • trunk/src/org/openstreetmap/josm/tools/PlatformHookUnixoid.java

    r1084 r1169  
    1515  */
    1616public class PlatformHookUnixoid implements PlatformHook {
    17         public void preStartupHook(){
    18         }
    19         public void startupHook() {
    20         }
    21         public void openUrl(String url) throws IOException {
    22                 String[] programs = {"gnome-open", "kfmclient openURL", "firefox"};
    23                 for (String program : programs) {
    24                         try {
    25                                 Runtime.getRuntime().exec(program+" "+url);
    26                                 return;
    27                         } catch (IOException e) {
    28                         }
    29                 }
    30         }
    31         public void initShortcutGroups() {
    32                 // This is the Windows list. Someone should look over it and make it more "*nix"...
    33                 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_DEFAULT+Shortcut.GROUP_NONE),    Integer.toString(-1));
    34                 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_DEFAULT+Shortcut.GROUP_HOTKEY),  Integer.toString(KeyEvent.CTRL_DOWN_MASK));
    35                 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_DEFAULT+Shortcut.GROUP_MENU),    Integer.toString(KeyEvent.CTRL_DOWN_MASK));
    36                 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_DEFAULT+Shortcut.GROUP_EDIT),    Integer.toString(0));
    37                 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_DEFAULT+Shortcut.GROUP_LAYER),   Integer.toString(KeyEvent.ALT_DOWN_MASK));
    38                 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_DEFAULT+Shortcut.GROUP_DIRECT),  Integer.toString(0));
    39                 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_DEFAULT+Shortcut.GROUP_MNEMONIC),Integer.toString(KeyEvent.ALT_DOWN_MASK));
     17    public void preStartupHook(){
     18    }
     19    public void startupHook() {
     20    }
     21    public void openUrl(String url) throws IOException {
     22        String[] programs = {"gnome-open", "kfmclient openURL", "firefox"};
     23        for (String program : programs) {
     24            try {
     25                Runtime.getRuntime().exec(program+" "+url);
     26                return;
     27            } catch (IOException e) {
     28            }
     29        }
     30    }
     31    public void initShortcutGroups() {
     32        // This is the Windows list. Someone should look over it and make it more "*nix"...
     33        Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_DEFAULT+Shortcut.GROUP_NONE),    Integer.toString(-1));
     34        Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_DEFAULT+Shortcut.GROUP_HOTKEY),  Integer.toString(KeyEvent.CTRL_DOWN_MASK));
     35        Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_DEFAULT+Shortcut.GROUP_MENU),    Integer.toString(KeyEvent.CTRL_DOWN_MASK));
     36        Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_DEFAULT+Shortcut.GROUP_EDIT),    Integer.toString(0));
     37        Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_DEFAULT+Shortcut.GROUP_LAYER),   Integer.toString(KeyEvent.ALT_DOWN_MASK));
     38        Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_DEFAULT+Shortcut.GROUP_DIRECT),  Integer.toString(0));
     39        Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_DEFAULT+Shortcut.GROUP_MNEMONIC),Integer.toString(KeyEvent.ALT_DOWN_MASK));
    4040
    41                 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT1+Shortcut.GROUP_NONE),       Integer.toString(-1));
    42                 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT1+Shortcut.GROUP_HOTKEY),     Integer.toString(KeyEvent.CTRL_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK));
    43                 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT1+Shortcut.GROUP_MENU),       Integer.toString(KeyEvent.CTRL_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK));
    44                 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT1+Shortcut.GROUP_EDIT),       Integer.toString(KeyEvent.SHIFT_DOWN_MASK));
    45                 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT1+Shortcut.GROUP_LAYER),      Integer.toString(KeyEvent.ALT_DOWN_MASK  | KeyEvent.SHIFT_DOWN_MASK));
    46                 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT1+Shortcut.GROUP_DIRECT),     Integer.toString(KeyEvent.SHIFT_DOWN_MASK));
    47                 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT1+Shortcut.GROUP_MNEMONIC),   Integer.toString(KeyEvent.ALT_DOWN_MASK));
     41        Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT1+Shortcut.GROUP_NONE),       Integer.toString(-1));
     42        Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT1+Shortcut.GROUP_HOTKEY),     Integer.toString(KeyEvent.CTRL_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK));
     43        Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT1+Shortcut.GROUP_MENU),       Integer.toString(KeyEvent.CTRL_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK));
     44        Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT1+Shortcut.GROUP_EDIT),       Integer.toString(KeyEvent.SHIFT_DOWN_MASK));
     45        Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT1+Shortcut.GROUP_LAYER),      Integer.toString(KeyEvent.ALT_DOWN_MASK  | KeyEvent.SHIFT_DOWN_MASK));
     46        Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT1+Shortcut.GROUP_DIRECT),     Integer.toString(KeyEvent.SHIFT_DOWN_MASK));
     47        Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT1+Shortcut.GROUP_MNEMONIC),   Integer.toString(KeyEvent.ALT_DOWN_MASK));
    4848
    49                 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT2+Shortcut.GROUP_NONE),       Integer.toString(-1));
    50                 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT2+Shortcut.GROUP_HOTKEY),     Integer.toString(KeyEvent.CTRL_DOWN_MASK | KeyEvent.ALT_DOWN_MASK));
    51                 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT2+Shortcut.GROUP_MENU),       Integer.toString(KeyEvent.CTRL_DOWN_MASK | KeyEvent.ALT_DOWN_MASK));
    52                 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT2+Shortcut.GROUP_EDIT),       Integer.toString(KeyEvent.ALT_DOWN_MASK  | KeyEvent.SHIFT_DOWN_MASK));
    53                 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT2+Shortcut.GROUP_LAYER),      Integer.toString(KeyEvent.ALT_DOWN_MASK));
    54                 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT2+Shortcut.GROUP_DIRECT),     Integer.toString(KeyEvent.CTRL_DOWN_MASK));
    55                 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT2+Shortcut.GROUP_MNEMONIC),   Integer.toString(KeyEvent.ALT_DOWN_MASK));
    56         }
    57         public void initSystemShortcuts() {
    58                 // TODO: Insert system shortcuts here. See Windows and espacially OSX to see how to.
    59         }
    60         /**
    61           * This should work for all platforms. Yeah, should.
    62           * See PlatformHook.java for a list of reasons why
    63           * this is implemented here...
    64           */
    65         public String makeTooltip(String name, Shortcut sc) {
    66                 String result = "";
    67                 result += "<html>";
    68                 result += name;
    69                 if (sc != null && sc.getKeyText().length() != 0) {
    70                         result += " ";
    71                         result += "<font size='-2'>";
    72                         result += "("+sc.getKeyText()+")";
    73                         result += "</font>";
    74                 }
    75                 result += "&nbsp;</html>";
    76                 return result;
    77         }
     49        Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT2+Shortcut.GROUP_NONE),       Integer.toString(-1));
     50        Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT2+Shortcut.GROUP_HOTKEY),     Integer.toString(KeyEvent.CTRL_DOWN_MASK | KeyEvent.ALT_DOWN_MASK));
     51        Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT2+Shortcut.GROUP_MENU),       Integer.toString(KeyEvent.CTRL_DOWN_MASK | KeyEvent.ALT_DOWN_MASK));
     52        Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT2+Shortcut.GROUP_EDIT),       Integer.toString(KeyEvent.ALT_DOWN_MASK  | KeyEvent.SHIFT_DOWN_MASK));
     53        Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT2+Shortcut.GROUP_LAYER),      Integer.toString(KeyEvent.ALT_DOWN_MASK));
     54        Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT2+Shortcut.GROUP_DIRECT),     Integer.toString(KeyEvent.CTRL_DOWN_MASK));
     55        Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT2+Shortcut.GROUP_MNEMONIC),   Integer.toString(KeyEvent.ALT_DOWN_MASK));
     56    }
     57    public void initSystemShortcuts() {
     58        // TODO: Insert system shortcuts here. See Windows and espacially OSX to see how to.
     59    }
     60    /**
     61      * This should work for all platforms. Yeah, should.
     62      * See PlatformHook.java for a list of reasons why
     63      * this is implemented here...
     64      */
     65    public String makeTooltip(String name, Shortcut sc) {
     66        String result = "";
     67        result += "<html>";
     68        result += name;
     69        if (sc != null && sc.getKeyText().length() != 0) {
     70            result += " ";
     71            result += "<font size='-2'>";
     72            result += "("+sc.getKeyText()+")";
     73            result += "</font>";
     74        }
     75        result += "&nbsp;</html>";
     76        return result;
     77    }
    7878}
  • trunk/src/org/openstreetmap/josm/tools/PlatformHookWindows.java

    r1084 r1169  
    1414  */
    1515public class PlatformHookWindows extends PlatformHookUnixoid implements PlatformHook {
    16         public void preStartupHook(){
    17         }
    18         public void startupHook() {
    19         }
    20         public void openUrl(String url) throws IOException {
    21                 Runtime.getRuntime().exec("rundll32 url.dll,FileProtocolHandler " + url);
    22         }
    23         public void initShortcutGroups() {
    24                 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_DEFAULT+Shortcut.GROUP_NONE),    Integer.toString(-1));
    25                 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_DEFAULT+Shortcut.GROUP_HOTKEY),  Integer.toString(KeyEvent.CTRL_DOWN_MASK));
    26                 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_DEFAULT+Shortcut.GROUP_MENU),    Integer.toString(KeyEvent.CTRL_DOWN_MASK));
    27                 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_DEFAULT+Shortcut.GROUP_EDIT),    Integer.toString(0));
    28                 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_DEFAULT+Shortcut.GROUP_LAYER),   Integer.toString(KeyEvent.ALT_DOWN_MASK));
    29                 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_DEFAULT+Shortcut.GROUP_DIRECT),  Integer.toString(0));
    30                 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_DEFAULT+Shortcut.GROUP_MNEMONIC),Integer.toString(KeyEvent.ALT_DOWN_MASK));
     16    public void preStartupHook(){
     17    }
     18    public void startupHook() {
     19    }
     20    public void openUrl(String url) throws IOException {
     21        Runtime.getRuntime().exec("rundll32 url.dll,FileProtocolHandler " + url);
     22    }
     23    public void initShortcutGroups() {
     24        Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_DEFAULT+Shortcut.GROUP_NONE),    Integer.toString(-1));
     25        Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_DEFAULT+Shortcut.GROUP_HOTKEY),  Integer.toString(KeyEvent.CTRL_DOWN_MASK));
     26        Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_DEFAULT+Shortcut.GROUP_MENU),    Integer.toString(KeyEvent.CTRL_DOWN_MASK));
     27        Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_DEFAULT+Shortcut.GROUP_EDIT),    Integer.toString(0));
     28        Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_DEFAULT+Shortcut.GROUP_LAYER),   Integer.toString(KeyEvent.ALT_DOWN_MASK));
     29        Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_DEFAULT+Shortcut.GROUP_DIRECT),  Integer.toString(0));
     30        Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_DEFAULT+Shortcut.GROUP_MNEMONIC),Integer.toString(KeyEvent.ALT_DOWN_MASK));
    3131
    32                 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT1+Shortcut.GROUP_NONE),       Integer.toString(-1));
    33                 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT1+Shortcut.GROUP_HOTKEY),     Integer.toString(KeyEvent.CTRL_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK));
    34                 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT1+Shortcut.GROUP_MENU),       Integer.toString(KeyEvent.CTRL_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK));
    35                 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT1+Shortcut.GROUP_EDIT),       Integer.toString(KeyEvent.SHIFT_DOWN_MASK));
    36                 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT1+Shortcut.GROUP_LAYER),      Integer.toString(KeyEvent.ALT_DOWN_MASK  | KeyEvent.SHIFT_DOWN_MASK));
    37                 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT1+Shortcut.GROUP_DIRECT),     Integer.toString(KeyEvent.SHIFT_DOWN_MASK));
    38                 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT1+Shortcut.GROUP_MNEMONIC),   Integer.toString(KeyEvent.ALT_DOWN_MASK));
     32        Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT1+Shortcut.GROUP_NONE),       Integer.toString(-1));
     33        Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT1+Shortcut.GROUP_HOTKEY),     Integer.toString(KeyEvent.CTRL_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK));
     34        Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT1+Shortcut.GROUP_MENU),       Integer.toString(KeyEvent.CTRL_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK));
     35        Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT1+Shortcut.GROUP_EDIT),       Integer.toString(KeyEvent.SHIFT_DOWN_MASK));
     36        Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT1+Shortcut.GROUP_LAYER),      Integer.toString(KeyEvent.ALT_DOWN_MASK  | KeyEvent.SHIFT_DOWN_MASK));
     37        Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT1+Shortcut.GROUP_DIRECT),     Integer.toString(KeyEvent.SHIFT_DOWN_MASK));
     38        Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT1+Shortcut.GROUP_MNEMONIC),   Integer.toString(KeyEvent.ALT_DOWN_MASK));
    3939
    40                 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT2+Shortcut.GROUP_NONE),       Integer.toString(-1));
    41                 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT2+Shortcut.GROUP_HOTKEY),     Integer.toString(KeyEvent.CTRL_DOWN_MASK | KeyEvent.ALT_DOWN_MASK));
    42                 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT2+Shortcut.GROUP_MENU),       Integer.toString(KeyEvent.CTRL_DOWN_MASK | KeyEvent.ALT_DOWN_MASK));
    43                 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT2+Shortcut.GROUP_EDIT),       Integer.toString(KeyEvent.ALT_DOWN_MASK  | KeyEvent.SHIFT_DOWN_MASK));
    44                 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT2+Shortcut.GROUP_LAYER),      Integer.toString(KeyEvent.ALT_DOWN_MASK));
    45                 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT2+Shortcut.GROUP_DIRECT),     Integer.toString(KeyEvent.CTRL_DOWN_MASK));
    46                 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT2+Shortcut.GROUP_MNEMONIC),   Integer.toString(KeyEvent.ALT_DOWN_MASK));
    47         }
    48         public void initSystemShortcuts() {
    49                 // This list if far from complete!
    50                 Shortcut.registerSystemShortcut("system:exit", "unused", KeyEvent.VK_F4, KeyEvent.ALT_DOWN_MASK).setAutomatic(); // items with automatic shortcuts will not be added to the menu bar at all
    51                 Shortcut.registerSystemShortcut("system:menuexit", "unused", KeyEvent.VK_Q, KeyEvent.CTRL_DOWN_MASK);
    52                 Shortcut.registerSystemShortcut("system:copy", "unused", KeyEvent.VK_C, KeyEvent.CTRL_DOWN_MASK);
    53                 Shortcut.registerSystemShortcut("system:paste", "unused", KeyEvent.VK_V, KeyEvent.CTRL_DOWN_MASK);
    54                 Shortcut.registerSystemShortcut("system:cut", "unused", KeyEvent.VK_X, KeyEvent.CTRL_DOWN_MASK);
    55                 Shortcut.registerSystemShortcut("system:duplicate", "unused", KeyEvent.VK_D, KeyEvent.CTRL_DOWN_MASK); // not really system, but to avoid odd results
    56                 Shortcut.registerSystemShortcut("system:help", "unused", KeyEvent.VK_F1, 0);
    57         }
     40        Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT2+Shortcut.GROUP_NONE),       Integer.toString(-1));
     41        Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT2+Shortcut.GROUP_HOTKEY),     Integer.toString(KeyEvent.CTRL_DOWN_MASK | KeyEvent.ALT_DOWN_MASK));
     42        Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT2+Shortcut.GROUP_MENU),       Integer.toString(KeyEvent.CTRL_DOWN_MASK | KeyEvent.ALT_DOWN_MASK));
     43        Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT2+Shortcut.GROUP_EDIT),       Integer.toString(KeyEvent.ALT_DOWN_MASK  | KeyEvent.SHIFT_DOWN_MASK));
     44        Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT2+Shortcut.GROUP_LAYER),      Integer.toString(KeyEvent.ALT_DOWN_MASK));
     45        Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT2+Shortcut.GROUP_DIRECT),     Integer.toString(KeyEvent.CTRL_DOWN_MASK));
     46        Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT2+Shortcut.GROUP_MNEMONIC),   Integer.toString(KeyEvent.ALT_DOWN_MASK));
     47    }
     48    public void initSystemShortcuts() {
     49        // This list if far from complete!
     50        Shortcut.registerSystemShortcut("system:exit", "unused", KeyEvent.VK_F4, KeyEvent.ALT_DOWN_MASK).setAutomatic(); // items with automatic shortcuts will not be added to the menu bar at all
     51        Shortcut.registerSystemShortcut("system:menuexit", "unused", KeyEvent.VK_Q, KeyEvent.CTRL_DOWN_MASK);
     52        Shortcut.registerSystemShortcut("system:copy", "unused", KeyEvent.VK_C, KeyEvent.CTRL_DOWN_MASK);
     53        Shortcut.registerSystemShortcut("system:paste", "unused", KeyEvent.VK_V, KeyEvent.CTRL_DOWN_MASK);
     54        Shortcut.registerSystemShortcut("system:cut", "unused", KeyEvent.VK_X, KeyEvent.CTRL_DOWN_MASK);
     55        Shortcut.registerSystemShortcut("system:duplicate", "unused", KeyEvent.VK_D, KeyEvent.CTRL_DOWN_MASK); // not really system, but to avoid odd results
     56        Shortcut.registerSystemShortcut("system:help", "unused", KeyEvent.VK_F1, 0);
     57    }
    5858}
    5959
  • trunk/src/org/openstreetmap/josm/tools/PrimaryDateParser.java

    r627 r1169  
    1515 * based on similar code in JOSM. This class is not threadsafe, a separate
    1616 * instance must be created per thread.
    17  * 
     17 *
    1818 * @author Brett Henderson
    1919 */
    2020public class PrimaryDateParser {
    21         private DatatypeFactory datatypeFactory;
    22         private FallbackDateParser fallbackDateParser;
    23         private Calendar calendar;
    24        
    25        
    26         /**
    27         * Creates a new instance.
    28         */
    29         public PrimaryDateParser() {
    30                 // Build an xml data type factory.
    31                 try {
    32                         datatypeFactory = DatatypeFactory.newInstance();
    33                        
    34                 } catch (DatatypeConfigurationException e) {
    35                         throw new RuntimeException("Unable to instantiate xml datatype factory.", e);
    36                 }
    37                
    38                 fallbackDateParser = new FallbackDateParser();
    39                
    40                 calendar = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
    41         }
    42        
    43        
    44         private boolean isDateInShortStandardFormat(String date) {
    45                 char[] dateChars;
    46                 // We can only parse the date if it is in a very specific format.
    47                 // eg. 2007-09-23T08:25:43Z
    48                
    49                 if (date.length() != 20) {
    50                         return false;
    51                 }
    52                
    53                 dateChars = date.toCharArray();
    54                
    55                 // Make sure any fixed characters are in the correct place.
    56                 if (dateChars[4] != '-') {
    57                         return false;
    58                 }
    59                 if (dateChars[7] != '-') {
    60                         return false;
    61                 }
    62                 if (dateChars[10] != 'T') {
    63                         return false;
    64                 }
    65                 if (dateChars[13] != ':') {
    66                         return false;
    67                 }
    68                 if (dateChars[16] != ':') {
    69                         return false;
    70                 }
    71                 if (dateChars[19] != 'Z') {
    72                         return false;
    73                 }
    74                
    75                 // Ensure all remaining characters are numbers.
    76                 for (int i = 0; i < 4; i++) {
    77                         if (dateChars[i] < '0' || dateChars[i] > '9') {
    78                                 return false;
    79                         }
    80                 }
    81                 for (int i = 5; i < 7; i++) {
    82                         if (dateChars[i] < '0' || dateChars[i] > '9') {
    83                                 return false;
    84                         }
    85                 }
    86                 for (int i = 8; i < 10; i++) {
    87                         if (dateChars[i] < '0' || dateChars[i] > '9') {
    88                                 return false;
    89                         }
    90                 }
    91                 for (int i = 11; i < 13; i++) {
    92                         if (dateChars[i] < '0' || dateChars[i] > '9') {
    93                                 return false;
    94                         }
    95                 }
    96                 for (int i = 14; i < 16; i++) {
    97                         if (dateChars[i] < '0' || dateChars[i] > '9') {
    98                                 return false;
    99                         }
    100                 }
    101                 for (int i = 17; i < 19; i++) {
    102                         if (dateChars[i] < '0' || dateChars[i] > '9') {
    103                                 return false;
    104                         }
    105                 }
    106                
    107                 // No problems found so it is in the special case format.
    108                 return true;
    109         }
    110        
    111        
    112         private boolean isDateInLongStandardFormat(String date) {
    113                 char[] dateChars;
    114                 // We can only parse the date if it is in a very specific format.
    115                 // eg. 2007-09-23T08:25:43.000Z
    116                
    117                 if (date.length() != 24) {
    118                         return false;
    119                 }
    120                
    121                 dateChars = date.toCharArray();
    122                
    123                 // Make sure any fixed characters are in the correct place.
    124                 if (dateChars[4] != '-') {
    125                         return false;
    126                 }
    127                 if (dateChars[7] != '-') {
    128                         return false;
    129                 }
    130                 if (dateChars[10] != 'T') {
    131                         return false;
    132                 }
    133                 if (dateChars[13] != ':') {
    134                         return false;
    135                 }
    136                 if (dateChars[16] != ':') {
    137                         return false;
    138                 }
    139                 if (dateChars[19] != '.') {
    140                         return false;
    141                 }
    142                 if (dateChars[23] != 'Z') {
    143                         return false;
    144                 }
    145                
    146                 // Ensure all remaining characters are numbers.
    147                 for (int i = 0; i < 4; i++) {
    148                         if (dateChars[i] < '0' || dateChars[i] > '9') {
    149                                 return false;
    150                         }
    151                 }
    152                 for (int i = 5; i < 7; i++) {
    153                         if (dateChars[i] < '0' || dateChars[i] > '9') {
    154                                 return false;
    155                         }
    156                 }
    157                 for (int i = 8; i < 10; i++) {
    158                         if (dateChars[i] < '0' || dateChars[i] > '9') {
    159                                 return false;
    160                         }
    161                 }
    162                 for (int i = 11; i < 13; i++) {
    163                         if (dateChars[i] < '0' || dateChars[i] > '9') {
    164                                 return false;
    165                         }
    166                 }
    167                 for (int i = 14; i < 16; i++) {
    168                         if (dateChars[i] < '0' || dateChars[i] > '9') {
    169                                 return false;
    170                         }
    171                 }
    172                 for (int i = 17; i < 19; i++) {
    173                         if (dateChars[i] < '0' || dateChars[i] > '9') {
    174                                 return false;
    175                         }
    176                 }
    177                 for (int i = 20; i < 23; i++) {
    178                         if (dateChars[i] < '0' || dateChars[i] > '9') {
    179                                 return false;
    180                         }
    181                 }
    182                
    183                 // No problems found so it is in the special case format.
    184                 return true;
    185         }
    186        
    187        
    188         private Date parseShortStandardDate(String date) {
    189                 int year;
    190                 int month;
    191                 int day;
    192                 int hour;
    193                 int minute;
    194                 int second;
    195                
    196                 year = Integer.parseInt(date.substring(0, 4));
    197                 month = Integer.parseInt(date.substring(5, 7));
    198                 day = Integer.parseInt(date.substring(8, 10));
    199                 hour = Integer.parseInt(date.substring(11, 13));
    200                 minute = Integer.parseInt(date.substring(14, 16));
    201                 second = Integer.parseInt(date.substring(17, 19));
    202                
    203                 calendar.clear();
    204                 calendar.set(Calendar.YEAR, year);
    205                 calendar.set(Calendar.MONTH, month - 1);
    206                 calendar.set(Calendar.DAY_OF_MONTH, day);
    207                 calendar.set(Calendar.HOUR_OF_DAY, hour);
    208                 calendar.set(Calendar.MINUTE, minute);
    209                 calendar.set(Calendar.SECOND, second);
    210                
    211                 return calendar.getTime();
    212         }
    213        
    214        
    215         private Date parseLongStandardDate(String date) {
    216                 int year;
    217                 int month;
    218                 int day;
    219                 int hour;
    220                 int minute;
    221                 int second;
    222                 int millisecond;
    223                
    224                 year = Integer.parseInt(date.substring(0, 4));
    225                 month = Integer.parseInt(date.substring(5, 7));
    226                 day = Integer.parseInt(date.substring(8, 10));
    227                 hour = Integer.parseInt(date.substring(11, 13));
    228                 minute = Integer.parseInt(date.substring(14, 16));
    229                 second = Integer.parseInt(date.substring(17, 19));
    230                 millisecond = Integer.parseInt(date.substring(20, 23));
    231                
    232                 calendar.clear();
    233                 calendar.set(Calendar.YEAR, year);
    234                 calendar.set(Calendar.MONTH, month - 1);
    235                 calendar.set(Calendar.DAY_OF_MONTH, day);
    236                 calendar.set(Calendar.HOUR_OF_DAY, hour);
    237                 calendar.set(Calendar.MINUTE, minute);
    238                 calendar.set(Calendar.SECOND, second);
    239                 calendar.set(Calendar.MILLISECOND, millisecond);
    240                
    241                 return calendar.getTime();
    242         }
    243        
    244        
    245         /**
    246         * Attempts to parse the specified date.
    247          *
    248         * @param date
    249         *            The date to parse.
    250         * @return The date.
    251         * @throws ParseException
    252         *             Occurs if the date does not match any of the supported date
    253         *             formats.
    254         */
    255         public Date parse(String date) throws ParseException {
    256                 try {
    257                         if (isDateInShortStandardFormat(date)) {
    258                                 return parseShortStandardDate(date);
    259                         } else if (isDateInLongStandardFormat(date)) {
    260                                 return parseLongStandardDate(date);
    261                         } else {
    262                                 return datatypeFactory.newXMLGregorianCalendar(date).toGregorianCalendar().getTime();
    263                         }
    264                        
    265                 } catch (IllegalArgumentException e) {
    266                         return fallbackDateParser.parse(date);
    267                 }
    268         }
     21    private DatatypeFactory datatypeFactory;
     22    private FallbackDateParser fallbackDateParser;
     23    private Calendar calendar;
     24
     25
     26    /**
     27    * Creates a new instance.
     28    */
     29    public PrimaryDateParser() {
     30        // Build an xml data type factory.
     31        try {
     32            datatypeFactory = DatatypeFactory.newInstance();
     33
     34        } catch (DatatypeConfigurationException e) {
     35            throw new RuntimeException("Unable to instantiate xml datatype factory.", e);
     36        }
     37
     38        fallbackDateParser = new FallbackDateParser();
     39
     40        calendar = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
     41    }
     42
     43
     44    private boolean isDateInShortStandardFormat(String date) {
     45        char[] dateChars;
     46        // We can only parse the date if it is in a very specific format.
     47        // eg. 2007-09-23T08:25:43Z
     48
     49        if (date.length() != 20) {
     50            return false;
     51        }
     52
     53        dateChars = date.toCharArray();
     54
     55        // Make sure any fixed characters are in the correct place.
     56        if (dateChars[4] != '-') {
     57            return false;
     58        }
     59        if (dateChars[7] != '-') {
     60            return false;
     61        }
     62        if (dateChars[10] != 'T') {
     63            return false;
     64        }
     65        if (dateChars[13] != ':') {
     66            return false;
     67        }
     68        if (dateChars[16] != ':') {
     69            return false;
     70        }
     71        if (dateChars[19] != 'Z') {
     72            return false;
     73        }
     74
     75        // Ensure all remaining characters are numbers.
     76        for (int i = 0; i < 4; i++) {
     77            if (dateChars[i] < '0' || dateChars[i] > '9') {
     78                return false;
     79            }
     80        }
     81        for (int i = 5; i < 7; i++) {
     82            if (dateChars[i] < '0' || dateChars[i] > '9') {
     83                return false;
     84            }
     85        }
     86        for (int i = 8; i < 10; i++) {
     87            if (dateChars[i] < '0' || dateChars[i] > '9') {
     88                return false;
     89            }
     90        }
     91        for (int i = 11; i < 13; i++) {
     92            if (dateChars[i] < '0' || dateChars[i] > '9') {
     93                return false;
     94            }
     95        }
     96        for (int i = 14; i < 16; i++) {
     97            if (dateChars[i] < '0' || dateChars[i] > '9') {
     98                return false;
     99            }
     100        }
     101        for (int i = 17; i < 19; i++) {
     102            if (dateChars[i] < '0' || dateChars[i] > '9') {
     103                return false;
     104            }
     105        }
     106
     107        // No problems found so it is in the special case format.
     108        return true;
     109    }
     110
     111
     112    private boolean isDateInLongStandardFormat(String date) {
     113        char[] dateChars;
     114        // We can only parse the date if it is in a very specific format.
     115        // eg. 2007-09-23T08:25:43.000Z
     116
     117        if (date.length() != 24) {
     118            return false;
     119        }
     120
     121        dateChars = date.toCharArray();
     122
     123        // Make sure any fixed characters are in the correct place.
     124        if (dateChars[4] != '-') {
     125            return false;
     126        }
     127        if (dateChars[7] != '-') {
     128            return false;
     129        }
     130        if (dateChars[10] != 'T') {
     131            return false;
     132        }
     133        if (dateChars[13] != ':') {
     134            return false;
     135        }
     136        if (dateChars[16] != ':') {
     137            return false;
     138        }
     139        if (dateChars[19] != '.') {
     140            return false;
     141        }
     142        if (dateChars[23] != 'Z') {
     143            return false;
     144        }
     145
     146        // Ensure all remaining characters are numbers.
     147        for (int i = 0; i < 4; i++) {
     148            if (dateChars[i] < '0' || dateChars[i] > '9') {
     149                return false;
     150            }
     151        }
     152        for (int i = 5; i < 7; i++) {
     153            if (dateChars[i] < '0' || dateChars[i] > '9') {
     154                return false;
     155            }
     156        }
     157        for (int i = 8; i < 10; i++) {
     158            if (dateChars[i] < '0' || dateChars[i] > '9') {
     159                return false;
     160            }
     161        }
     162        for (int i = 11; i < 13; i++) {
     163            if (dateChars[i] < '0' || dateChars[i] > '9') {
     164                return false;
     165            }
     166        }
     167        for (int i = 14; i < 16; i++) {
     168            if (dateChars[i] < '0' || dateChars[i] > '9') {
     169                return false;
     170            }
     171        }
     172        for (int i = 17; i < 19; i++) {
     173            if (dateChars[i] < '0' || dateChars[i] > '9') {
     174                return false;
     175            }
     176        }
     177        for (int i = 20; i < 23; i++) {
     178            if (dateChars[i] < '0' || dateChars[i] > '9') {
     179                return false;
     180            }
     181        }
     182
     183        // No problems found so it is in the special case format.
     184        return true;
     185    }
     186
     187
     188    private Date parseShortStandardDate(String date) {
     189        int year;
     190        int month;
     191        int day;
     192        int hour;
     193        int minute;
     194        int second;
     195
     196        year = Integer.parseInt(date.substring(0, 4));
     197        month = Integer.parseInt(date.substring(5, 7));
     198        day = Integer.parseInt(date.substring(8, 10));
     199        hour = Integer.parseInt(date.substring(11, 13));
     200        minute = Integer.parseInt(date.substring(14, 16));
     201        second = Integer.parseInt(date.substring(17, 19));
     202
     203        calendar.clear();
     204        calendar.set(Calendar.YEAR, year);
     205        calendar.set(Calendar.MONTH, month - 1);
     206        calendar.set(Calendar.DAY_OF_MONTH, day);
     207        calendar.set(Calendar.HOUR_OF_DAY, hour);
     208        calendar.set(Calendar.MINUTE, minute);
     209        calendar.set(Calendar.SECOND, second);
     210
     211        return calendar.getTime();
     212    }
     213
     214
     215    private Date parseLongStandardDate(String date) {
     216        int year;
     217        int month;
     218        int day;
     219        int hour;
     220        int minute;
     221        int second;
     222        int millisecond;
     223
     224        year = Integer.parseInt(date.substring(0, 4));
     225        month = Integer.parseInt(date.substring(5, 7));
     226        day = Integer.parseInt(date.substring(8, 10));
     227        hour = Integer.parseInt(date.substring(11, 13));
     228        minute = Integer.parseInt(date.substring(14, 16));
     229        second = Integer.parseInt(date.substring(17, 19));
     230        millisecond = Integer.parseInt(date.substring(20, 23));
     231
     232        calendar.clear();
     233        calendar.set(Calendar.YEAR, year);
     234        calendar.set(Calendar.MONTH, month - 1);
     235        calendar.set(Calendar.DAY_OF_MONTH, day);
     236        calendar.set(Calendar.HOUR_OF_DAY, hour);
     237        calendar.set(Calendar.MINUTE, minute);
     238        calendar.set(Calendar.SECOND, second);
     239        calendar.set(Calendar.MILLISECOND, millisecond);
     240
     241        return calendar.getTime();
     242    }
     243
     244
     245    /**
     246    * Attempts to parse the specified date.
     247     *
     248    * @param date
     249    *            The date to parse.
     250    * @return The date.
     251    * @throws ParseException
     252    *             Occurs if the date does not match any of the supported date
     253    *             formats.
     254    */
     255    public Date parse(String date) throws ParseException {
     256        try {
     257            if (isDateInShortStandardFormat(date)) {
     258                return parseShortStandardDate(date);
     259            } else if (isDateInLongStandardFormat(date)) {
     260                return parseLongStandardDate(date);
     261            } else {
     262                return datatypeFactory.newXMLGregorianCalendar(date).toGregorianCalendar().getTime();
     263            }
     264
     265        } catch (IllegalArgumentException e) {
     266            return fallbackDateParser.parse(date);
     267        }
     268    }
    269269}
  • trunk/src/org/openstreetmap/josm/tools/Shortcut.java

    r1157 r1169  
    139139     */
    140140    public KeyStroke getKeyStroke() {
    141         if (assignedModifier != -1) 
    142             return KeyStroke.getKeyStroke(assignedKey, assignedModifier);               
     141        if (assignedModifier != -1)
     142            return KeyStroke.getKeyStroke(assignedKey, assignedModifier);
    143143        return null;
    144144    }
     
    351351            System.err.println("CONFLICT WITH SYSTEM KEY "+shortText);
    352352            return null;
    353         } 
     353        }
    354354        potentialShortcut = new Shortcut(shortText, longText, key, GROUP_RESERVED, key, modifier, true, false);
    355355        shortcuts.put(shortText, potentialShortcut);
     
    441441    // a lengthy warning message
    442442    private static void displayWarning(Shortcut conflictsWith, Shortcut potentialShortcut, String shortText, String longText) {
    443         JOptionPane.showMessageDialog(Main.parent, 
     443        JOptionPane.showMessageDialog(Main.parent,
    444444                tr("Setting the keyboard shortcut ''{0}'' for the action ''{1}'' ({2}) failed\n"+
    445445                        "because the shortcut is already taken by the action ''{3}'' ({4}).\n\n",
  • trunk/src/org/openstreetmap/josm/tools/UrlLabel.java

    r627 r1169  
    1212public class UrlLabel extends JEditorPane implements HyperlinkListener {
    1313
    14         private final String url;
     14    private final String url;
    1515
    16         public UrlLabel(String url) {
    17                 this (url, url);
    18         }
     16    public UrlLabel(String url) {
     17        this (url, url);
     18    }
    1919
    20         public UrlLabel(String url, String description) {
    21                 this.url = url;
    22                 setContentType("text/html");
    23                 setText("<html><a href=\""+url+"\">"+description+"</a></html>");
    24                 setToolTipText(url);
    25                 setEditable(false);
    26                 setOpaque(false);
    27                 addHyperlinkListener(this);
    28         }
     20    public UrlLabel(String url, String description) {
     21        this.url = url;
     22        setContentType("text/html");
     23        setText("<html><a href=\""+url+"\">"+description+"</a></html>");
     24        setToolTipText(url);
     25        setEditable(false);
     26        setOpaque(false);
     27        addHyperlinkListener(this);
     28    }
    2929
    30         public void hyperlinkUpdate(HyperlinkEvent e) {
    31                 if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
    32                         OpenBrowser.displayUrl(url);
    33                 }
    34         }
     30    public void hyperlinkUpdate(HyperlinkEvent e) {
     31        if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
     32            OpenBrowser.displayUrl(url);
     33        }
     34    }
    3535}
  • trunk/src/org/openstreetmap/josm/tools/WikiReader.java

    r740 r1169  
    1414public class WikiReader {
    1515
    16         public static final String JOSM_EXTERN = "http://josm-extern.";
    17         private final String baseurl;
     16    public static final String JOSM_EXTERN = "http://josm-extern.";
     17    private final String baseurl;
    1818
    19         public WikiReader(String baseurl) {
    20                 this.baseurl = baseurl;
     19    public WikiReader(String baseurl) {
     20        this.baseurl = baseurl;
    2121    }
    2222
    23         /**
    24         * Read the page specified by the url and return the content.
    25         *
    26         * If the url is within the baseurl path, parse it as an trac wikipage and
    27         * replace relative pathes etc..
    28         *
    29         * @return Either the string of the content of the wiki page.
    30         * @throws IOException Throws, if the page could not be loaded.
    31         */
    32         public String read(String url) throws IOException {
     23    /**
     24    * Read the page specified by the url and return the content.
     25    *
     26    * If the url is within the baseurl path, parse it as an trac wikipage and
     27    * replace relative pathes etc..
     28    *
     29    * @return Either the string of the content of the wiki page.
     30    * @throws IOException Throws, if the page could not be loaded.
     31    */
     32    public String read(String url) throws IOException {
    3333        BufferedReader in = new BufferedReader(new InputStreamReader(new URL(url).openStream(), "utf-8"));
    3434        if (url.startsWith(baseurl))
    35                 return readFromTrac(in, url);
     35            return readFromTrac(in, url);
    3636        return readNormal(in);
    37         }
    38 
    39         private String readNormal(BufferedReader in) throws IOException {
    40                 StringBuilder b = new StringBuilder("<html>");
    41                 for (String line = in.readLine(); line != null; line = in.readLine()) {
    42                         line = adjustText(line);
    43                         b.append(line);
    44                         b.append("\n");
    45                 }
    46                 return b.toString();
    4737    }
    4838
    49         private String readFromTrac(BufferedReader in, String url) throws IOException {
     39    private String readNormal(BufferedReader in) throws IOException {
     40        StringBuilder b = new StringBuilder("<html>");
     41        for (String line = in.readLine(); line != null; line = in.readLine()) {
     42            line = adjustText(line);
     43            b.append(line);
     44            b.append("\n");
     45        }
     46        return b.toString();
     47    }
     48
     49    private String readFromTrac(BufferedReader in, String url) throws IOException {
    5050        boolean inside = false;
    5151        StringBuilder b = new StringBuilder("<html>");
    5252        for (String line = in.readLine(); line != null; line = in.readLine()) {
    53                 if (line.contains("<div id=\"searchable\">"))
    54                         inside = true;
    55                 else if (line.contains("<div class=\"buttons\">"))
    56                         inside = false;
    57                 if (inside) {
    58                         line = line.replaceAll("<img src=\"/", "<img src=\""+baseurl+"/");
    59                         line = line.replaceAll("href=\"/", "href=\""+baseurl+"/");
    60                         if (!line.contains("$"))
    61                                 line = line.replaceAll("<p>Describe \"([^\"]+)\" here</p>", "<p>Describe \"$1\" <a href=\""+JOSM_EXTERN+url.substring(7)+"\">here</a></p>");
    62                         line = adjustText(line);
    63                         b.append(line);
    64                         b.append("\n");
    65                 }
     53            if (line.contains("<div id=\"searchable\">"))
     54                inside = true;
     55            else if (line.contains("<div class=\"buttons\">"))
     56                inside = false;
     57            if (inside) {
     58                line = line.replaceAll("<img src=\"/", "<img src=\""+baseurl+"/");
     59                line = line.replaceAll("href=\"/", "href=\""+baseurl+"/");
     60                if (!line.contains("$"))
     61                    line = line.replaceAll("<p>Describe \"([^\"]+)\" here</p>", "<p>Describe \"$1\" <a href=\""+JOSM_EXTERN+url.substring(7)+"\">here</a></p>");
     62                line = adjustText(line);
     63                b.append(line);
     64                b.append("\n");
     65            }
    6666        }
    6767        b.append("</html>");
     
    6969    }
    7070
    71         private String adjustText(String text) {
    72             text = text.replaceAll(" />", ">");
    73             return text;
     71    private String adjustText(String text) {
     72        text = text.replaceAll(" />", ">");
     73        return text;
    7474    }
    7575}
  • trunk/src/org/openstreetmap/josm/tools/XmlObjectParser.java

    r1163 r1169  
    3131
    3232        public static final String lang = Main.getLanguageCode();
    33         public static class Uniform<T> implements Iterable<T>{
    34                 private Iterator<Object> iterator;
    35                 /**
    36                 * @param klass This has to be specified since generics are ereased from
    37                 * class files so the JVM cannot deduce T itself.
    38                 */
    39                 public Uniform(Reader input, String tagname, Class<T> klass) {
    40                         XmlObjectParser parser = new XmlObjectParser();
    41                         parser.map(tagname, klass);
    42                         parser.start(input);
    43                         iterator = parser.iterator();
    44                 }
    45                 public Iterator<T> iterator() {
    46                         return new Iterator<T>(){
    47                                 public boolean hasNext() {return iterator.hasNext();}
    48                                 @SuppressWarnings("unchecked") public T next() {return (T)iterator.next();}
    49                                 public void remove() {iterator.remove();}
    50                         };
    51                 }
    52         }
    53 
    54         private class Parser extends DefaultHandler {
    55                 Stack<Object> current = new Stack<Object>();
    56                 String characters = "";
    57                 @Override public void startElement(String ns, String lname, String qname, Attributes a) throws SAXException {
    58                         if (mapping.containsKey(qname)) {
    59                                 Class<?> klass = mapping.get(qname).klass;
    60                                 try {
    61                                         current.push(klass.newInstance());
    62                                 } catch (Exception e) {
    63                                         throw new SAXException(e);
    64                                 }
    65                                 for (int i = 0; i < a.getLength(); ++i)
    66                                         setValue(a.getQName(i), a.getValue(i));
    67                                 if (mapping.get(qname).onStart)
    68                                         report();
    69                                 if (mapping.get(qname).both)
    70                                 {
    71                                         try {
    72                                                 queue.put(current.peek());
    73                                         } catch (InterruptedException e) {
    74                                         }
    75                                 }
    76                         }
    77                 }
    78                 @Override public void endElement(String ns, String lname, String qname) throws SAXException {
    79                         if (mapping.containsKey(qname) && !mapping.get(qname).onStart)
    80                                 report();
    81                         else if (characters != null && !current.isEmpty()) {
    82                                 setValue(qname, characters.trim());
    83                                 characters = "";
    84                         }
    85                 }
    86                 @Override public void characters(char[] ch, int start, int length) {
    87                         String s = new String(ch, start, length);
    88                         characters += s;
    89                 }
    90 
    91                 private void report() {
    92                         try {
    93                                 queue.put(current.pop());
    94                         } catch (InterruptedException e) {
    95                         }
    96                         characters = "";
    97                 }
    98 
    99                 private Object getValueForClass(Class<?> klass, String value) {
    100                         if (klass == Boolean.TYPE)
    101                                 return parseBoolean(value);
    102                         else if (klass == Integer.TYPE || klass == Long.TYPE)
    103                                 return Long.parseLong(value);
    104                         else if (klass == Float.TYPE || klass == Double.TYPE)
    105                                 return Double.parseDouble(value);
    106                         return value;
    107                 }
    108                
    109                 private void setValue(String fieldName, String value) throws SAXException {
    110                         if (fieldName.equals("class") || fieldName.equals("default") || fieldName.equals("throw") || fieldName.equals("new") || fieldName.equals("null"))
    111                                 fieldName += "_";
    112                         try {
    113                                 Object c = current.peek();
    114                                 Field f = null;
    115                                 try {
    116                                         f = c.getClass().getField(fieldName);
    117                                 } catch (NoSuchFieldException e) {
    118                                         if(fieldName.startsWith(lang))
    119                                         {
    120                                                 String locfieldName = "locale_" +
    121                                                 fieldName.substring(lang.length());
    122                                                 try {
    123                                                         f = c.getClass().getField(locfieldName);
    124                                                 } catch (NoSuchFieldException ex) {
    125                                                 }
    126                                         }
    127                                 }
    128                                 if (f != null && Modifier.isPublic(f.getModifiers()))
    129                                         f.set(c, getValueForClass(f.getType(), value));
    130                                 else {
    131                                         if(fieldName.startsWith(lang))
    132                                         {
    133                                                 int l = lang.length();
    134                                                 fieldName = "set" + fieldName.substring(l,l+1).toUpperCase() + fieldName.substring(l+1);
    135                                         }
    136                                         else
    137                                         {
    138                                                 fieldName = "set" + fieldName.substring(0,1).toUpperCase() + fieldName.substring(1);
    139                                         }
    140                                         Method[] methods = c.getClass().getDeclaredMethods();
    141                                         for (Method m : methods) {
    142                                                 if (m.getName().equals(fieldName) && m.getParameterTypes().length == 1) {
    143                                                         m.invoke(c, new Object[]{getValueForClass(m.getParameterTypes()[0], value)});
    144                                                         return;
    145                                                 }
    146                                         }
    147                                 }
    148                         } catch (Exception e) {
    149                                 e.printStackTrace(); // SAXException does not dump inner exceptions.
    150                                 throw new SAXException(e);
    151                         }
    152                 }
    153                 private boolean parseBoolean(String s) {
    154                         return s != null &&
    155                                 !s.equals("0") &&
    156                                 !s.startsWith("off") &&
    157                                 !s.startsWith("false") &&
    158                                 !s.startsWith("no");
    159                 }
    160         }
    161 
    162         private static class Entry {
    163                 Class<?> klass;
    164                 boolean onStart;
    165                 boolean both;
    166                 public Entry(Class<?> klass, boolean onStart, boolean both) {
    167                         super();
    168                         this.klass = klass;
    169                         this.onStart = onStart;
    170                         this.both = both;
    171                 }
    172         }
    173 
    174         private Map<String, Entry> mapping = new HashMap<String, Entry>();
    175         private Parser parser;
    176        
    177         /**
    178         * The queue of already parsed items from the parsing thread.
    179         */
    180         private BlockingQueue<Object> queue = new ArrayBlockingQueue<Object>(10);
    181 
    182         /**
    183         * This stores one item retrieved from the queue to give hasNext a chance.
    184         * So this is also the object that will be returned on the next call to next().
    185         */
    186         private Object lookAhead = null;
    187        
    188         /**
    189         * This object represent the end of the stream (null is not allowed as
    190         * member in class Queue).
    191         */
    192         private Object EOS = new Object();
    193 
    194         public XmlObjectParser() {
    195                 parser = new Parser();
    196         }
    197 
    198         public Iterable<Object> start(final Reader in) {
    199                 new Thread(){
    200                         @Override public void run() {
    201                                 try {
    202                                 SAXParserFactory.newInstance().newSAXParser().parse(new InputSource(in), parser);
    203                                 } catch (Exception e) {
    204                                         try {
    205                                                 queue.put(e);
    206                                         } catch (InterruptedException e1) {
    207                                         }
    208                                 }
    209                                 parser = null;
    210                                 try {
    211                                         queue.put(EOS);
    212                                 } catch (InterruptedException e) {
    213                                 }
    214                         }
    215                 }.start();
    216                 return this;
    217         }
    218 
    219         public void map(String tagName, Class<?> klass) {
    220                 mapping.put(tagName, new Entry(klass,false,false));
    221         }
    222 
    223         public void mapOnStart(String tagName, Class<?> klass) {
    224                 mapping.put(tagName, new Entry(klass,true,false));
    225         }
    226 
    227         public void mapBoth(String tagName, Class<?> klass) {
    228                 mapping.put(tagName, new Entry(klass,false,true));
    229         }
    230 
    231         /**
    232         * @return The next object from the xml stream or <code>null</code>,
    233         * if no more objects.
    234         */
    235         public Object next() throws SAXException {
    236                 fillLookAhead();
    237                 if (lookAhead == EOS)
    238                         throw new NoSuchElementException();
    239                 Object o = lookAhead;
    240                 lookAhead = null;
    241                 return o;
    242         }
    243 
    244         private void fillLookAhead() throws SAXException {
    245                 if (lookAhead != null)
    246                         return;
    247             try {
    248                         lookAhead = queue.take();
    249                         if (lookAhead instanceof SAXException)
    250                                 throw (SAXException)lookAhead;
    251                         else if (lookAhead instanceof RuntimeException)
    252                                 throw (RuntimeException)lookAhead;
    253                         else if (lookAhead instanceof Exception)
    254                                 throw new SAXException((Exception)lookAhead);
    255                 } catch (InterruptedException e) {
    256                 throw new RuntimeException("XmlObjectParser must not be interrupted.", e);
    257                 }
    258     }
    259 
    260         public boolean hasNext() throws SAXException {
    261                 fillLookAhead();
     33    public static class Uniform<T> implements Iterable<T>{
     34        private Iterator<Object> iterator;
     35        /**
     36        * @param klass This has to be specified since generics are ereased from
     37        * class files so the JVM cannot deduce T itself.
     38        */
     39        public Uniform(Reader input, String tagname, Class<T> klass) {
     40            XmlObjectParser parser = new XmlObjectParser();
     41            parser.map(tagname, klass);
     42            parser.start(input);
     43            iterator = parser.iterator();
     44        }
     45        public Iterator<T> iterator() {
     46            return new Iterator<T>(){
     47                public boolean hasNext() {return iterator.hasNext();}
     48                @SuppressWarnings("unchecked") public T next() {return (T)iterator.next();}
     49                public void remove() {iterator.remove();}
     50            };
     51        }
     52    }
     53
     54    private class Parser extends DefaultHandler {
     55        Stack<Object> current = new Stack<Object>();
     56        String characters = "";
     57        @Override public void startElement(String ns, String lname, String qname, Attributes a) throws SAXException {
     58            if (mapping.containsKey(qname)) {
     59                Class<?> klass = mapping.get(qname).klass;
     60                try {
     61                    current.push(klass.newInstance());
     62                } catch (Exception e) {
     63                    throw new SAXException(e);
     64                }
     65                for (int i = 0; i < a.getLength(); ++i)
     66                    setValue(a.getQName(i), a.getValue(i));
     67                if (mapping.get(qname).onStart)
     68                    report();
     69                if (mapping.get(qname).both)
     70                {
     71                    try {
     72                        queue.put(current.peek());
     73                    } catch (InterruptedException e) {
     74                    }
     75                }
     76            }
     77        }
     78        @Override public void endElement(String ns, String lname, String qname) throws SAXException {
     79            if (mapping.containsKey(qname) && !mapping.get(qname).onStart)
     80                report();
     81            else if (characters != null && !current.isEmpty()) {
     82                setValue(qname, characters.trim());
     83                characters = "";
     84            }
     85        }
     86        @Override public void characters(char[] ch, int start, int length) {
     87            String s = new String(ch, start, length);
     88            characters += s;
     89        }
     90
     91        private void report() {
     92            try {
     93                queue.put(current.pop());
     94            } catch (InterruptedException e) {
     95            }
     96            characters = "";
     97        }
     98
     99        private Object getValueForClass(Class<?> klass, String value) {
     100            if (klass == Boolean.TYPE)
     101                return parseBoolean(value);
     102            else if (klass == Integer.TYPE || klass == Long.TYPE)
     103                return Long.parseLong(value);
     104            else if (klass == Float.TYPE || klass == Double.TYPE)
     105                return Double.parseDouble(value);
     106            return value;
     107        }
     108
     109        private void setValue(String fieldName, String value) throws SAXException {
     110            if (fieldName.equals("class") || fieldName.equals("default") || fieldName.equals("throw") || fieldName.equals("new") || fieldName.equals("null"))
     111                fieldName += "_";
     112            try {
     113                Object c = current.peek();
     114                Field f = null;
     115                try {
     116                    f = c.getClass().getField(fieldName);
     117                } catch (NoSuchFieldException e) {
     118                    if(fieldName.startsWith(lang))
     119                    {
     120                        String locfieldName = "locale_" +
     121                        fieldName.substring(lang.length());
     122                        try {
     123                            f = c.getClass().getField(locfieldName);
     124                        } catch (NoSuchFieldException ex) {
     125                        }
     126                    }
     127                }
     128                if (f != null && Modifier.isPublic(f.getModifiers()))
     129                    f.set(c, getValueForClass(f.getType(), value));
     130                else {
     131                    if(fieldName.startsWith(lang))
     132                    {
     133                        int l = lang.length();
     134                        fieldName = "set" + fieldName.substring(l,l+1).toUpperCase() + fieldName.substring(l+1);
     135                    }
     136                    else
     137                    {
     138                        fieldName = "set" + fieldName.substring(0,1).toUpperCase() + fieldName.substring(1);
     139                    }
     140                    Method[] methods = c.getClass().getDeclaredMethods();
     141                    for (Method m : methods) {
     142                        if (m.getName().equals(fieldName) && m.getParameterTypes().length == 1) {
     143                            m.invoke(c, new Object[]{getValueForClass(m.getParameterTypes()[0], value)});
     144                            return;
     145                        }
     146                    }
     147                }
     148            } catch (Exception e) {
     149                e.printStackTrace(); // SAXException does not dump inner exceptions.
     150                throw new SAXException(e);
     151            }
     152        }
     153        private boolean parseBoolean(String s) {
     154            return s != null &&
     155                !s.equals("0") &&
     156                !s.startsWith("off") &&
     157                !s.startsWith("false") &&
     158                !s.startsWith("no");
     159        }
     160    }
     161
     162    private static class Entry {
     163        Class<?> klass;
     164        boolean onStart;
     165        boolean both;
     166        public Entry(Class<?> klass, boolean onStart, boolean both) {
     167            super();
     168            this.klass = klass;
     169            this.onStart = onStart;
     170            this.both = both;
     171        }
     172    }
     173
     174    private Map<String, Entry> mapping = new HashMap<String, Entry>();
     175    private Parser parser;
     176
     177    /**
     178    * The queue of already parsed items from the parsing thread.
     179    */
     180    private BlockingQueue<Object> queue = new ArrayBlockingQueue<Object>(10);
     181
     182    /**
     183    * This stores one item retrieved from the queue to give hasNext a chance.
     184    * So this is also the object that will be returned on the next call to next().
     185    */
     186    private Object lookAhead = null;
     187
     188    /**
     189    * This object represent the end of the stream (null is not allowed as
     190    * member in class Queue).
     191    */
     192    private Object EOS = new Object();
     193
     194    public XmlObjectParser() {
     195        parser = new Parser();
     196    }
     197
     198    public Iterable<Object> start(final Reader in) {
     199        new Thread(){
     200            @Override public void run() {
     201                try {
     202                    SAXParserFactory.newInstance().newSAXParser().parse(new InputSource(in), parser);
     203                } catch (Exception e) {
     204                    try {
     205                        queue.put(e);
     206                    } catch (InterruptedException e1) {
     207                    }
     208                }
     209                parser = null;
     210                try {
     211                    queue.put(EOS);
     212                } catch (InterruptedException e) {
     213                }
     214            }
     215        }.start();
     216        return this;
     217    }
     218
     219    public void map(String tagName, Class<?> klass) {
     220        mapping.put(tagName, new Entry(klass,false,false));
     221    }
     222
     223    public void mapOnStart(String tagName, Class<?> klass) {
     224        mapping.put(tagName, new Entry(klass,true,false));
     225    }
     226
     227    public void mapBoth(String tagName, Class<?> klass) {
     228        mapping.put(tagName, new Entry(klass,false,true));
     229    }
     230
     231    /**
     232    * @return The next object from the xml stream or <code>null</code>,
     233    * if no more objects.
     234    */
     235    public Object next() throws SAXException {
     236        fillLookAhead();
     237        if (lookAhead == EOS)
     238            throw new NoSuchElementException();
     239        Object o = lookAhead;
     240        lookAhead = null;
     241        return o;
     242    }
     243
     244    private void fillLookAhead() throws SAXException {
     245        if (lookAhead != null)
     246            return;
     247        try {
     248            lookAhead = queue.take();
     249            if (lookAhead instanceof SAXException)
     250                throw (SAXException)lookAhead;
     251            else if (lookAhead instanceof RuntimeException)
     252                throw (RuntimeException)lookAhead;
     253            else if (lookAhead instanceof Exception)
     254                throw new SAXException((Exception)lookAhead);
     255        } catch (InterruptedException e) {
     256            throw new RuntimeException("XmlObjectParser must not be interrupted.", e);
     257        }
     258    }
     259
     260    public boolean hasNext() throws SAXException {
     261        fillLookAhead();
    262262        return lookAhead != EOS;
    263         }
    264 
    265         public Iterator<Object> iterator() {
    266                 return new Iterator<Object>(){
    267                         public boolean hasNext() {
    268                                 try {
    269                                         return XmlObjectParser.this.hasNext();
    270                                 } catch (SAXException e) {
    271                                         e.printStackTrace();
    272                                         throw new RuntimeException(e);
    273                                 }
    274                         }
    275                         public Object next() {
    276                                 try {
    277                                         return XmlObjectParser.this.next();
    278                                 } catch (SAXException e) {
    279                                         e.printStackTrace();
    280                                         throw new RuntimeException(e);
    281                                 }
    282                         }
    283                         public void remove() {
    284                                 throw new UnsupportedOperationException();
    285                         }
    286                 };
    287         }
     263    }
     264
     265    public Iterator<Object> iterator() {
     266        return new Iterator<Object>(){
     267            public boolean hasNext() {
     268                try {
     269                    return XmlObjectParser.this.hasNext();
     270                } catch (SAXException e) {
     271                    e.printStackTrace();
     272                    throw new RuntimeException(e);
     273                }
     274            }
     275            public Object next() {
     276                try {
     277                    return XmlObjectParser.this.next();
     278                } catch (SAXException e) {
     279                    e.printStackTrace();
     280                    throw new RuntimeException(e);
     281                }
     282            }
     283            public void remove() {
     284                throw new UnsupportedOperationException();
     285            }
     286        };
     287    }
    288288}
Note: See TracChangeset for help on using the changeset viewer.