001    package edu.nrao.sss.model.source.experiment;
002    
003    import java.awt.Dimension;
004    import java.awt.Graphics;
005    import java.awt.Graphics2D;
006    import java.util.Collection;
007    
008    import javax.swing.JComponent;
009    import javax.swing.ProgressMonitor;
010    import javax.swing.SwingWorker;
011    
012    import edu.nrao.sss.model.source.Source;
013    
014    /**
015     * A component that shows the location of sources relative to a central
016     * position.
017     * <p>
018     * This map allows users to move the center of the map by clicking on
019     * a location that becomes the new center.  It also allows the user
020     * to zoom in or out by using a mouse wheel.  This map pops up
021     * information about a source when it is right-clicked.</p>
022     * <p>
023     * <i>This class is a stubbed prototype.  It's API needs many additions
024     * before it is ready for production.</i></p>
025     * <p>
026     * <b><u><span style="font-variant:small-caps">Mouse Actions</span></u></b>
027     * <dl>
028     *   <dt>Single Left Click</dt>
029     *     <dd>Causes the point under the mouse pointer to become the new
030     *         center of the map.</dd>
031     *   <br/>
032     *   <dt>Single Right Click</dt>
033     *     <dd>Pops up information about the source under the mouse pointer.
034     *         <i>(At this time a window pops up no matter where the mouse
035     *             pointer is, and it contains a temporary fixed message.)</i></dd>
036     *   <br/>
037     *   <dt>Mouse Wheel Movement</dt>
038     *     <dl>
039     *       <dt><i>While pressing neither <tt>shift</tt> key nor <tt>control</tt> key:</i></dt>
040     *         <dd>Zooms the map in or out while maintaining the same center.</dd>
041     *       <dt><i>While pressing <tt>shift</tt> key, but not <tt>control</tt> key:</i></dt>
042     *         <dd>Changes the latitude of the center of the map, without changing
043     *             either the longitude or the radius.</dd>
044     *       <dt><i>While pressing <tt>control</tt> key, but not <tt>shift</tt> key:</i></dt>
045     *         <dd>Changes the longitude of the center of the map, without changing
046     *             either the latitude or the radius.</d>
047     *       <dt><i>While pressing both <tt>shift</tt> key and <tt>control</tt> key:</i></dt>
048     *         <dd>Changes both the longitude and latitude of the center of the map,
049     *             without changing the radius.</dd>
050     *     </dl>
051     * </dl>
052     * <p>
053     * <b>Version Info:</b>
054     * <table style="margin-left:2em">
055     *   <tr><td>$Revision: 1710 $</td></tr>
056     *   <tr><td>$Date: 2008-11-14 11:54:07 -0700 (Fri, 14 Nov 2008) $</td></tr>
057     *   <tr><td>$Author: dharland $ (last person to modify)</td></tr>
058     * </table></p>
059     * 
060     * @author David M. Harland
061     * @since 2007-06-07
062     */
063    public class SourceMapComponent
064      extends JComponent
065    {
066      private static final long serialVersionUID = 1L;
067    
068      protected SourceMap map;
069      
070      //============================================================================
071      // 
072      //============================================================================
073    
074      /** Creates a new instance. */
075      public SourceMapComponent()
076      {
077        map = new SourceMap();
078        
079        configure();
080      }
081    
082      /** Initializes UI properties at time of construction. */
083      private void configure()
084      {
085        setDoubleBuffered(true);
086    
087        Dimension prefSize = new Dimension(map.getWidth(), map.getHeight());
088        setPreferredSize(prefSize);
089        setSize(prefSize);
090        
091        new SourceMapMouseControl(this);  //Calls back to this.addMouseXxxListener
092      }
093    
094      //============================================================================
095      // PAINTING
096      //============================================================================
097      
098      @Override
099      public void paintComponent(Graphics g)
100      {
101        Graphics2D g2 = (Graphics2D)g;
102        
103        super.paintComponent(g2);
104    
105        map.setSize(this.getSize());
106        
107        map.paint(g2);
108      }
109      
110      /**
111       * Allows the time consuming task of adding lots of new sources to
112       * be performed in a thread other than the event dispatch thread.
113       */
114      private class AddSourceTask
115        extends SwingWorker<Void, Void>
116      {
117        private SourceMapComponent owner;
118        private Collection<? extends Source> newSources;
119        private String collectionName;
120        
121        public AddSourceTask(SourceMapComponent owner)
122        {
123          this.owner = owner;
124          collectionName = null;
125        }
126        
127        private ProgressMonitor monitor;
128        
129        void setRawMaterial(Collection<? extends Source> newSources)
130        {
131          this.newSources = newSources;
132        }
133        
134        void setCollectionName(String newName)
135        {
136          collectionName = (newName == null) ? "Anonymous" : newName;
137        }
138        
139        public Void doInBackground()
140        {
141          int srcCnt = newSources.size();
142          
143          monitor =
144            new ProgressMonitor(owner, "Adding " + srcCnt + " New Sources to Map",
145                                "Added 0 sources so far...", 0, newSources.size());
146          
147          int displayIncr = 10;
148          
149          if (srcCnt >= 1000)
150          {
151            displayIncr = 100;
152            monitor.setMillisToDecideToPopup(0);
153            monitor.setMillisToPopup(0);
154          }
155          else if (srcCnt <= 20)
156          {
157            displayIncr = 1;
158          }
159          
160          int s=0;
161          for (Source src : newSources)
162          {
163            owner.map.addSource(src, collectionName);
164            
165            monitor.setProgress(++s);
166    
167            if (s % displayIncr == 0)
168              monitor.setNote("Added " + s + " sources so far...");
169          }
170          
171          monitor.close();
172          
173          return null;
174        }
175        
176        @Override
177        public void done()
178        {
179          owner.repaint();
180        }
181      } //end class AddSourceTask
182    
183      //============================================================================
184      // WRAPPING SourceMap 
185      //============================================================================
186    
187      /**
188       * @param newSource
189       */
190      public void addSource(Source newSource)
191      {
192        map.addSource(newSource);
193      }
194      
195      /**
196       * @param newSources
197       */
198      public void addSources(Collection<? extends Source> newSources)
199      {
200        addSources(newSources, null);
201      }
202    
203      /**
204       * @param newSource
205       * @param collectionName
206       */
207      public void addSource(Source newSource, String collectionName)
208      {
209        map.addSource(newSource, collectionName);
210      }
211    
212      /**
213       * @param newSources
214       * @param collectionName
215       */
216      public void addSources(Collection<? extends Source> newSources,
217                             String                       collectionName)
218      {
219        //A SwingWorker that does the time consuming task of adding sources to
220        //this map in a separate thread.
221        AddSourceTask sourceAdder = new AddSourceTask(this);
222    
223        sourceAdder.setCollectionName(collectionName);
224        sourceAdder.setRawMaterial(newSources);
225        sourceAdder.execute();
226      }
227    
228      //============================================================================
229      // 
230      //============================================================================
231    
232      public StringBuilder appendHtmlMouseHelp(StringBuilder dest)
233      {
234        if (dest == null)
235          dest = new StringBuilder();
236        
237        //TODO This text s/b in an external store of some kind
238    
239        dest.append("<dl>");
240        dest.append("<dt><b>Single Left Click</b></dt>");
241        dest.append("<dd>Causes the point under the mouse pointer to become the new");
242        dest.append(" center of the map.</dd>");
243        dest.append("<dt><br/><b>Single Right Click</b></dt>");
244        dest.append("<dd>Pops up information about the source under the mouse pointer.");
245        dest.append(" <i>(At this time a window pops up no matter where the mouse");
246        dest.append(" pointer is, and it contains a temporary fixed message.)</i></dd>");
247        dest.append("<dt><br/><b>Mouse Wheel Movement</b></dt>");
248        dest.append("<dl>");
249        dest.append("<dt><i>While pressing neither <tt>shift</tt> key nor <tt>control</tt> key:</i></dt>");
250        dest.append("<dd>Zooms the map in or out while maintaining the same center.</dd>");
251        dest.append("<dt><i>While pressing <tt>shift</tt> key, but not <tt>control</tt> key:</i></dt>");
252        dest.append("<dd>Changes the latitude of the center of the map, without changing");
253        dest.append(" either the longitude or the radius.</dd>");
254        dest.append("<dt><i>While pressing <tt>control</tt> key, but not <tt>shift</tt> key:</i></dt>");
255        dest.append("<dd>Changes the longitude of the center of the map, without changing");
256        dest.append(" either the latitude or the radius.</dd>");
257        dest.append("<dt><i>While pressing both <tt>shift</tt> key and <tt>control</tt> key:</i></dt>");
258        dest.append("<dd>Changes both the longitude and latitude of the center of the map,");
259        dest.append(" without changing the radius.</dd>");
260        dest.append("</dl>");
261        dest.append("</dl>");
262    
263        
264        return dest;
265      }
266    }