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/tools
Files:
23 edited

Legend:

Unmodified
Added
Removed
  • 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.