Statistics
| Revision:

root / branches / v2_0_0_prep / libraries / libFMap_controls / src / org / gvsig / fmap / mapcontrol / MapControl.java @ 30349

History | View | Annotate | Download (72.5 KB)

1
/* gvSIG. Sistema de Informaci?n Geogr?fica de la Generalitat Valenciana
2
 *
3
 * Copyright (C) 2004 IVER T.I. and Generalitat Valenciana.
4
 *
5
 * This program is free software; you can redistribute it and/or
6
 * modify it under the terms of the GNU General Public License
7
 * as published by the Free Software Foundation; either version 2
8
 * of the License, or (at your option) any later version.
9
 *
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 * GNU General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU General Public License
16
 * along with this program; if not, write to the Free Software
17
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,USA.
18
 *
19
 * For more information, contact:
20
 *
21
 *  Generalitat Valenciana
22
 *   Conselleria d'Infraestructures i Transport
23
 *   Av. Blasco Ib??ez, 50
24
 *   46010 VALENCIA
25
 *   SPAIN
26
 *
27
 *      +34 963862235
28
 *   gvsig@gva.es
29
 *      www.gvsig.gva.es
30
 *
31
 *    or
32
 *
33
 *   IVER T.I. S.A
34
 *   Salamanca 50
35
 *   46005 Valencia
36
 *   Spain
37
 *
38
 *   +34 963163400
39
 *   dac@iver.es
40
 */
41
package org.gvsig.fmap.mapcontrol;
42

    
43
import java.awt.Color;
44
import java.awt.Cursor;
45
import java.awt.Dimension;
46
import java.awt.Graphics;
47
import java.awt.Graphics2D;
48
import java.awt.Image;
49
import java.awt.Point;
50
import java.awt.Toolkit;
51
import java.awt.event.ActionEvent;
52
import java.awt.event.ActionListener;
53
import java.awt.event.ComponentEvent;
54
import java.awt.event.ComponentListener;
55
import java.awt.event.MouseEvent;
56
import java.awt.event.MouseListener;
57
import java.awt.event.MouseMotionListener;
58
import java.awt.event.MouseWheelEvent;
59
import java.awt.event.MouseWheelListener;
60
import java.awt.geom.Point2D;
61
import java.awt.image.BufferedImage;
62
import java.awt.image.MemoryImageSource;
63
import java.util.ArrayList;
64
import java.util.Comparator;
65
import java.util.HashMap;
66
import java.util.Set;
67
import java.util.TreeMap;
68
import java.util.prefs.Preferences;
69

    
70
import javax.swing.JComponent;
71
import javax.swing.Timer;
72

    
73
import org.cresques.cts.IProjection;
74
import org.gvsig.fmap.crs.CRSFactory;
75
import org.gvsig.fmap.dal.DataStoreNotification;
76
import org.gvsig.fmap.dal.feature.FeatureStoreNotification;
77
import org.gvsig.fmap.geom.Geometry;
78
import org.gvsig.fmap.geom.GeometryLocator;
79
import org.gvsig.fmap.geom.GeometryManager;
80
import org.gvsig.fmap.geom.Geometry.SUBTYPES;
81
import org.gvsig.fmap.geom.exception.CreateEnvelopeException;
82
import org.gvsig.fmap.geom.primitive.Envelope;
83
import org.gvsig.fmap.geom.util.Converter;
84
import org.gvsig.fmap.mapcontext.MapContext;
85
import org.gvsig.fmap.mapcontext.ViewPort;
86
import org.gvsig.fmap.mapcontext.events.AtomicEvent;
87
import org.gvsig.fmap.mapcontext.events.listeners.AtomicEventListener;
88
import org.gvsig.fmap.mapcontext.layers.FLayers;
89
import org.gvsig.fmap.mapcontext.layers.LayerCollectionEvent;
90
import org.gvsig.fmap.mapcontext.layers.LayerEvent;
91
import org.gvsig.fmap.mapcontext.layers.SpatialCache;
92
import org.gvsig.fmap.mapcontext.layers.vectorial.FLyrVect;
93
import org.gvsig.fmap.mapcontext.layers.vectorial.GraphicLayer;
94
import org.gvsig.fmap.mapcontrol.tools.BehaviorException;
95
import org.gvsig.fmap.mapcontrol.tools.CompoundBehavior;
96
import org.gvsig.fmap.mapcontrol.tools.Behavior.Behavior;
97
import org.gvsig.fmap.mapcontrol.tools.Listeners.ToolListener;
98
import org.gvsig.fmap.mapcontrol.tools.grid.Grid;
99
import org.gvsig.fmap.mapcontrol.tools.snapping.snappers.ISnapper;
100
import org.gvsig.fmap.mapcontrol.tools.snapping.snappers.ISnapperRaster;
101
import org.gvsig.fmap.mapcontrol.tools.snapping.snappers.ISnapperVectorial;
102
import org.gvsig.tools.observer.Observable;
103
import org.gvsig.tools.observer.Observer;
104
import org.gvsig.tools.task.Cancellable;
105
import org.gvsig.utils.exceptionHandling.ExceptionHandlingSupport;
106
import org.gvsig.utils.exceptionHandling.ExceptionListener;
107
import org.slf4j.Logger;
108
import org.slf4j.LoggerFactory;
109

    
110

    
111

    
112
/**
113
 * <p>A component that includes a {@link MapContext MapContext} with support for use it as a particular {@link Behavior Behavior}.</p>
114
 *
115
 * <p>A developer can register a set of <code>Behavior</code>, but only one (that can be a composition of several) of them can be active. The active one
116
 *  defines the way to work and access with its <code>MapContext</code>'s layers. The active behavior, in combination with the appropriate
117
 *  {@link ToolListener ToolListener} will allow user work with a particular <i>tool</i>.</p>
118
 *
119
 * <p>All mouse events produced on this component will be delegated to the current active behavior, the <i>currentMapTool</i>.</p>
120
 *
121
 * <p><b>Drawing process:</b></p>
122
 *
123
 * <p>Uses a double buffer for the drawing process of <code>MapContext</code>'s information.</p>
124
 *
125
 * <p>If the double buffer wasn't created, creates a new one.</p>
126
 *
127
 * <p>Paints the component according the following algorithm:
128
 * <br>
129
 *  &nbsp If <i>status</i> is <i>UPDATED</i>:<br>
130
 *  &nbsp &nbsp If there is a <i>double buffer</i>:<br>
131
 *  &nbsp &nbsp &nbsp If there is a <i>behavior</i> for managing the <code>MapControl</code> instance, delegates
132
 *   the drawing process to that behavior, calling: <code><i>behavior_instance</i>.paintComponent(g)</code>.<br>
133
 *  &nbsp &nbsp &nbsp Else, repaints the current graphical information quickly calling: <code>g.drawImage(image,0,0,null)</code>.<br>
134
 *  &nbsp Else, (<i>status</i> is <i>OUTDATED</i>, or <i>ONLY_GRAPHICS</i>): executes a quickly repaint of the previous information calling <code>g.drawImage(image,0,0,null)</code>, and creates
135
 *   a <i>painting request</i> to delegate the heavy drawing process to the {@link Drawer2 Drawer2}'s worker thread, according the <i>SingleWorketThread</i> pattern, starting a timer to update
136
 *   (invoking <code>repaint()</code>) the view every delay of <code>1000 / drawFrameRate</code> ms. during that heavy drawing process, and if its enabled <code>drawAnimationEnabled</code>. The <i>painting request</i> once is being attended, invokes <code>MapContext</code> to
137
 *   draw the layers: <code>mapContext.draw(image, g, cancel,mapContext.getScaleView());</code>
138
 * <br>
139
 * <p>Some notes:
140
 *  <ul>
141
 *   <li>The painting process can be cancelled calling {@link #cancelDrawing() #cancelDrawing()}.</li>
142
 *   <li>At last resort, the particular implementation of each layer in a <code>MapControl</code>'s <code>MapContrext</code>
143
 *    will be that one which will draw the graphical information, and, if supports, which could cancel its drawing subprocess.</li>
144
 *   <li>It's possible to force repaint all layers, calling {@link #drawMap(boolean doClear) #drawMap(boolean)}.</li>
145
 *   <li>It's possible repaint only the dirty layers, calling {@link #rePaintDirtyLayers() #rePaintDirtyLayers()}.</li>
146
 *   <li>It's possible repaint only the {@link GraphicLayer GraphicLayer}, calling {@link #drawGraphics() #drawGraphics()}.</li>
147
 *  </ul>
148
 * </p>
149
 *
150
 * <p><b>Tools:</b></p>
151
 *
152
 * <p>A developer can:
153
 *   <ul>
154
 *    <li>Register each tool as:
155
 *     <ul>
156
 *      <li>A single behavior: {@link #addBehavior(String, Behavior) #addMapTool(String, Behavior)}.</li>
157
 *      <li>Or, a compound behavior: {@link #addBehavior(String, Behavior) #addMapTool(String, Behavior)}.</li>
158
 *     </ul>
159
 *    </li>
160
 *    <li>Get the current active tool: {@link #getCurrentMapTool() #getCurrentMapTool()}.</li>
161
 *    <li>Get the current active tool name: {@link #getCurrentTool() #getCurrentTool()}.</li>
162
 *    <li>Get a registered tool: {@link #getMapTool(String) #getMapTool(String)}.</li>
163
 *    <li>Get the name of all tools registered: {@link #getMapToolsKeySet() #getMapToolsKeySet()}.</li>
164
 *    <li>Get all tools registered, including the name they were registered: {@link #getNamesMapTools() #getNamesMapTools()}.</li>
165
 *    <li>Determine if has a tool registered: {@link #hasTool(String) #hasTool(String)}.</li>
166
 *    <li>Set as an active tool, one of the registered: {@link #setTool(String) #setTool(String)}.</li>
167
 *    <li>Set as active tool, the previous used: {@link #setPrevTool() #setPrevTool()}.</li>
168
 *    <li>Set the current tool: {@link #setCurrentMapTool(Behavior) #setCurrentMapTool(Behavior)}.</li>
169
 *    <li>Change the draw frame rate: {@link #setDrawFrameRate(int) #setDrawFrameRate(int)} and {@link #applyFrameRate() #applyFrameRate()}.</li>
170
 *    <li>Get the draw frame rate: {@link #getDrawFrameRate() #getDrawFrameRate()}.</li>
171
 *    <li>Determine if will repaint this component each time timer finishes: {@link #isDrawAnimationEnabled() #isDrawAnimationEnabled()}.</li>
172
 *    <li>Change if will repaint this component each time timer finishes: {@link #setDrawAnimationEnabled(boolean) #setDrawAnimationEnabled(boolean)}.</li>
173
 *    <li>Get the shared object that determines if a drawing process must be cancelled or can continue: {@link #getCanceldraw() #getCanceldraw()}.</li>
174
 *    <li>Get the combined tool: {@link #getCombinedTool() #getCombinedTool()}.</li>
175
 *    <li>Set a combined tool: {@link #setCombinedTool(Behavior) #setCombinedTool(Behavior)}.</li>
176
 *    <li>Remove the combined tool: {@link #removeCombinedTool() #removeCombinedTool()}.</li>
177
 *   </ul>
178
 * </p>
179
 *
180
 * <p><b>Exception listener:</b></p>
181
 *
182
 * <p> Adding an <code>ExceptionListener</code>, can get notification about any exception produced:
183
 *  <ul>
184
 *   <li>Attending a <i>painting request</i>.</li>
185
 *   <li>Working with the active tool.</li>
186
 *   <li>Applying a <i>zoom in</i> or <i>zoom out</i> operation.</li>
187
 *  </ul>
188
 * </p>
189
 *
190
 * <p><b>Other:</b></p>
191
 *
192
 * <p>Other useful capabilities of <code>MapControl</code>:
193
 *   <ul>
194
 *    <li>Cancel the current drawing process (notifying it also to the inner
195
 *     <code>MapContext</code> instance and its layers): {@link #cancelDrawing() #cancelDrawing()}.</li>
196
 *    <li>Applying a <i>zoom in</i> operation centered at mouse position (without a <code>ToolListener</code>): {@link #zoomIn() #zoomIn()}.</li>
197
 *    <li>Applying a <i>zoom out</i> operation centered at mouse position (without a <code>ToolListener</code>): {@link #zoomOut() #zoomOut()}.</li>
198
 *   </ul>
199
 * </p>
200
 *
201
 * @see CancelDraw
202
 * @see Drawer
203
 * @see MapContextListener
204
 * @see MapToolListener
205
 *
206
 * @author Fernando Gonz?lez Cort?s
207
 * @author Pablo Piqueras Bartolom? (pablo.piqueras@iver.es)
208
 */
209
public class MapControl extends JComponent implements ComponentListener, Observer {
210
        protected static final GeometryManager geomManager = GeometryLocator.getGeometryManager();
211
        protected static final Logger logger = LoggerFactory.getLogger(GeometryManager.class);
212

    
213
        /**
214
         * <p>One of the possible status of <code>MapControl</code>. Determines that all visible information has been
215
         * drawn and its updated.</p>
216
         */
217
        public static final int ACTUALIZADO = 0;
218

    
219
        /**
220
         * <p>One of the possible status of <code>MapControl</code>. Determines that not all visible information has been
221
         * drawn or isn't updated.</p>
222
         */
223
        public static final int DESACTUALIZADO = 1;
224

    
225
        /**
226
         * <p>One of the possible status of <code>MapControl</code>. Determines that only the graphical layer must
227
         * be drawn / updated.</p>
228
         */
229
        public static final int ONLY_GRAPHICS = 2;
230

    
231
        /**
232
         * <p>Determines if the drawer can update this <code>MapControl</code> instance when the timer launches an event.</p>
233
         */
234
        private static boolean drawAnimationEnabled = true;
235

    
236
        /**
237
         * <p>Inner model with the layers, event support for drawing them, and the <code>ViewPort</code>
238
         *  with information to adapt to the bounds available in <i>image coordinates</i>.</p>
239
         *
240
         * @see #getMapContext()
241
         * @see #setMapContext(MapContext)
242
         */
243
        private MapContext mapContext = null;
244

    
245
        /**
246
         * <p>All registered <code>Behavior</code> that can define a way to work with this <code>MapControl</code>.</p>
247
         *
248
         * <p>Only one of them can be active at a given moment.</p>
249
         *
250
         * @see #addBehavior(String, Behavior)
251
         * @see #addBehavior(String, Behavior[])
252
         * @see #getMapTool(String)
253
         * @see #getMapToolsKeySet()
254
         * @see #getNamesMapTools()
255
         */
256
        protected HashMap namesMapTools = new HashMap();
257

    
258
        /**
259
         * <p>Active {@link Behavior Behavior} that will generate events according a criterion, and then, with a {@link ToolListener ToolListener}
260
         *  associated, will simulate to user that works with this component as a particular tool.</p>
261
         *
262
         * @see #getCurrentMapTool()
263
         * @see #getCurrentTool()
264
         * @see #setTool(String)
265
         */
266
        protected Behavior currentMapTool = null;
267

    
268
        /**
269
         * <p>Determines which's the current drawn status of this component:
270
         * <ul>
271
         *  <li><b>OUTDATED</b>: all visible information has been drawn or isn't updated.</li>
272
         *  <li><b>UTDATED</b>: all visible information has been drawn and its updated.</li>
273
         *  <li><b>ONLY_GRAPHICS</b>: only the graphical layer must be drawn / updated.</li>
274
         * </ul>
275
         * </p>
276
         *
277
         * <p>The <code>MapControl</code> drawing process will consider the value of this parameter to decide which elements will
278
         *  be updated or drawn.</p>
279
         */
280
        private int status = DESACTUALIZADO;
281

    
282
        /**
283
         * <p>Image with a buffer to accelerate the draw the changes of the graphical items in this component.</p>
284
         *
285
         * <p>Firstly, information will be drawn in the buffer, and, when is outright drawn, that information will be displayed.
286
         * Meanwhile, the previous image can be kept showed.</p>
287
         *
288
         * @see BufferedImage
289
         *
290
         * @see #getImage()
291
         */
292
        private BufferedImage image = null;
293

    
294
        /**
295
         * <p>Name of the tool used currently to interact with this component.</p>
296
         *
297
         * @see #getCurrentTool()
298
         * @see #setTool(String)
299
         */
300
        protected String currentTool;
301

    
302
        /**
303
         * <p>Object to store the flag that notifies a drawing thread task and <code>MapContext</code>'s layers,
304
         * that must be canceled or can continue with the process.</p>
305
         *
306
         * @see #cancelDrawing()
307
         */
308
        private CancelDraw canceldraw;
309

    
310
        //private boolean isCancelled = true;
311

    
312
        /**
313
         * <p>Fires an action events after a specified delay.</p>
314
         *
315
         * <p><code>MapControl</code> will use the timer to update its visible graphical information during
316
         *  a drawing process, or allowing to cancel that process.</p>
317
         *
318
         * <p>This is very useful to pretend faster interactivity to user when <code>MapControl</code> has
319
         *  lots of layers, and / or layers with heavy graphical elements, that need a long time to finish
320
         *  drawing all its data.</p>
321
         */
322
        private Timer timer;
323

    
324
        /**
325
         * <p>Reference to the {@link ViewPort ViewPort} of the {@link MapContext MapContext} of this component.</p>
326
         *
327
         * <p>The view port once is created an instance of <code>MapControl</code>,
328
         *  is obtained from the <i>EPSG:23030</i> projection, that's the default projection for this component.</p>
329
         *
330
         * <p>After, the view port will change adapting itself according the current projection and the extent.</p>
331
         *
332
         * @see #getViewPort()
333
         *
334
         * @see ViewPort
335
         */
336
        protected ViewPort vp;
337

    
338
        /**
339
         * <p>Manager of all <code>MapControl</code> painting requests.</p>
340
         */
341
        private Drawer drawer;
342

    
343
        /**
344
         * <p>Listener of all kind of mouse events produced in this component.</p>
345
         *
346
         * <p>Delegates each mouse event to the current map tool.</p>
347
         *
348
         * @see #addBehavior(String, Behavior)
349
         * @see #addBehavior(String, Behavior[])
350
         * @see #getMapTool(String)
351
         * @see #getMapToolsKeySet()
352
         * @see #getNamesMapTools()
353
         * @see #setTool(String)
354
         */
355
        protected MapToolListener mapToolListener = new MapToolListener();
356

    
357
        /**
358
         * <p>Listener of all events produced in a this component's <code>MapContext</code>
359
         * object during an atomic period of time.</p>
360
         */
361
        private MapContextListener mapContextListener = new MapContextListener();
362

    
363
        /**
364
         * <p>Group of <code>ExceptionListener</code> that, in whatever moment could be notified a Throwable Java error or exception.</p>
365
         *
366
         * @see #addExceptionListener(ExceptionListener)
367
         * @see #removeExceptionListener(ExceptionListener)
368
         */
369
        private ExceptionHandlingSupport exceptionHandlingSupport = new ExceptionHandlingSupport();
370

    
371
        /**
372
         * <p>Name of the previous tool used.</p>
373
         */
374
        protected String prevTool;
375

    
376
        /**
377
         * <p>Tool that will be used combined with the current tool of this <code>MapControl</code>.</p>
378
         */
379
        private Behavior combinedTool = null;
380

    
381
        /**
382
         * Optional grid that could be applied on the <code>MapControl</code>'s view port.
383
         *
384
         * @see #getGrid()
385
         * @see #setAdjustGrid(boolean)
386
         */
387
        private Grid cadgrid = new Grid();
388
        /**
389
         * Represents the cursor's point selected in <i>screen coordinates</i>.
390
         *
391
         * @see ViewPort#fromMapPoint(Point2D)
392
         */
393
        private Point2D adjustedPoint;
394
        /**
395
         * <p>Determines if the position of the snap of the mouse's cursor on the <code>MapControl</code>
396
         * is within the area around a control point of a geometry.</p>
397
         *
398
         * <p>The area is calculated as a circle centered at the control point and with radius the pixels tolerance
399
         *  defined in the preferences.</p>
400
         */
401
        private boolean bForceCoord = false;
402

    
403
        /**
404
         * Kind of geometry drawn to identify the kind of control point selected by the cursor's mouse.
405
         */
406
        private ISnapper usedSnap = null;
407

    
408
        /**
409
         * Determines if the snap tools are enabled or disabled.
410
         *
411
         * @see #isRefentEnabled()
412
         * @see #setRefentEnabled(boolean)
413
         */
414
        private boolean bRefent = true;
415

    
416
        /**
417
         * Stores the 2D map coordinates of the last point added.
418
         */
419
        private double[] previousPoint = null;
420

    
421
        protected static MapControlManager mapControlManager = MapControlLocator.getMapControlManager();
422

    
423
        private static TreeMap selected = new TreeMap(new Comparator(){
424

    
425
                public int compare(Object o1, Object o2) {
426
                        if (o1.getClass().equals(o2.getClass()))
427
                                return 0;
428
                        if (((ISnapper)o1).getPriority()>((ISnapper)o2).getPriority())
429
                                return 1;
430
                        else
431
                                return -1;
432
                }
433

    
434
        });
435

    
436
        /**
437
         * Represents the cursor's point selected in <i>map coordinates</i>.
438
         *
439
         * @see MapControl#toMapPoint
440
         */
441
        private Point2D mapAdjustedPoint;
442

    
443
        /**
444
         * Renderer used to draw the layers.
445
         */
446
        private MapControlDrawer mapControlDrawer = null;
447

    
448
        /**
449
         * <p>Creates a new <code>MapControl</code> instance with the following characteristics:
450
         * <ul>
451
         *  <li><i>Name</i>: MapControl .</li>
452
         *  <li>Disables the double buffer of <code>JComponent</code> .</li>
453
         *  <li>Sets opaque <i>(see {@link JComponent#setOpaque(boolean)} )</i>. </li>
454
         *  <li>Sets its status to <code>OUTDATED</code> .</li>
455
         *  <li>Creates a new {@link CancelDraw CancelDraw} object to notify <code>MapContext</code>'s layers if can continue processing the drawn or must cancel it.</li>
456
         *  <li>Creates a new {@link MapContext MapContext} with a new {@link ViewPort ViewPort} in the projection <i>"EPSG:23030"</i> .</li>
457
         *  <li>Creates a new {@link CommandListener CommandListener} for edition operations.</li>
458
         *  <li>Creates a new {@link MapToolListener MapToolListener}, and associates it as a listener of whatever kind of mouse events produced in this component.</li>
459
         *  <li>Creates a new {@link Drawer2 Drawer2} for managing the painting requests.</li>
460
         *  <li>Creates a new timer that will invoke refresh this component <code>drawFrameRate</code> per second, when is running a drawing process, and its enabled
461
         *   <code>drawAnimationEnabled</code>.</li>
462
         * </ul>
463
         * </p>
464
         */
465
        public MapControl() {
466
                this.setName("MapControl");
467
                setDoubleBuffered(false);
468
                setOpaque(true);
469
                status = DESACTUALIZADO;
470

    
471
                //Clase usada para cancelar el dibujado
472
                canceldraw = new CancelDraw();
473

    
474
                //Modelo de datos y ventana del mismo
475
                // TODO: Cuando creamos un mapControl, deber?amos asignar
476
                // la projecci?n por defecto con la que vayamos a trabajar.
477
                // 23030 es el c?digo EPSG del UTM30 elipsoide ED50
478
                vp = new ViewPort(CRSFactory.getCRS("EPSG:23030"));
479
                setMapContext(new MapContext(vp));
480

    
481
                //eventos
482
                this.addComponentListener(this);
483
                this.addMouseListener(mapToolListener);
484
                this.addMouseMotionListener(mapToolListener);
485
                this.addMouseWheelListener(mapToolListener);
486

    
487
                this.drawer = new Drawer();
488
                //Timer para mostrar el redibujado mientras se dibuja
489
                timer = new Timer(1000/MapContext.getDrawFrameRate(),
490
                                new ActionListener() {
491
                        public void actionPerformed(ActionEvent e) {
492

    
493
                                if (drawAnimationEnabled) {
494
                                        MapControl.this.repaint();
495
                                }
496
                        }
497
                });
498
                initializeGrid();
499
        }
500

    
501
        /**
502
         * <p>Sets a <code>MapContext</code> to this component.</p>
503
         *
504
         * <p>The <code>MapContext</code> has the <i>model</i>, and most of the <i>view</i>,
505
         * and <i>control</i> logic of the layers of this component, including a {@link ViewPort ViewPort} to adapt the
506
         * information to the projection, and to display it in the available area.</p>
507
         *
508
         * <p>If <code>model</code> hadn't a <code>ViewPort</code>, assigns the current one to it, otherwise, use its <code>ViewPort</code>.</p>
509
         *
510
         * <p>After assigning the <code>MapContext</code> and <code>ViewPort</code>, sets the same {@link MapContextListener MapContextListener}
511
         *  that was using, and changes the <i>status</i> to <code>OUTDATED</code>.</p>
512
         *
513
         * @param model this component's <code>MapContext</code>, that includes the <code>ViewPort</code>.
514
         *
515
         * @see MapContext
516
         *
517
         * @see #getMapContext()
518
         */
519
        public void setMapContext(MapContext model) {
520
                if (mapContext != null) {
521
                        mapContext.removeAtomicEventListener(mapContextListener);
522
                }
523

    
524
                mapContext = model;
525

    
526
                if (mapContext.getViewPort() == null) {
527
                        mapContext.setViewPort(vp);
528
                } else {
529
                        vp = mapContext.getViewPort();
530
                        cadgrid.setViewPort(vp);
531
                }        
532

    
533
                mapContext.addAtomicEventListener(mapContextListener);
534

    
535
                status = DESACTUALIZADO;
536
        }
537

    
538
        /**
539
         * @return the mapControlDrawer
540
         */
541
        public MapControlDrawer getMapControlDrawer() {
542
                return mapControlDrawer;
543
        }
544

    
545
        /**
546
         * @param mapControlDrawer the mapControlDrawer to set
547
         */
548
        public void setMapControlDrawer(MapControlDrawer mapControlDrawer) {
549
                this.mapControlDrawer = mapControlDrawer;
550
                this.mapControlDrawer.setViewPort(vp);
551
        }
552

    
553
        /**
554
         * <p>Gets this component's {@link MapContext MapContext} projection.</p>
555
         *
556
         * @return this component's {@link MapContext MapContext} projection
557
         *
558
         * @see MapContext#getProjection()
559
         * @see MapControl#setProjection(IProjection)
560
         */
561
        public IProjection getProjection() {
562
                return getMapContext().getProjection();
563
        }
564

    
565
        /**
566
         * <p>Sets the projection to this component's {@link MapContext MapContext}.</p>
567
         *
568
         * @param proj the kind of projection to this component's {@link MapContext MapContext}
569
         *
570
         * @see MapContext#setProjection(IProjection)
571
         * @see MapControl#getProjection()
572
         */
573
        public void setProjection(IProjection proj) {
574
                getMapContext().setProjection(proj);
575
        }
576

    
577
        /**
578
         * <p>Gets this component's <code>MapContext</code>, with the <i>model</i>, and most of the <i>view</i>,
579
         * and <i>control</i> logic of the layers of this component, including a {@link ViewPort ViewPort} to adapt the
580
         * information to the projection, and display it in the available area.</p>
581
         *
582
         * @return this component's <code>MapContext</code>, that includes the <code>ViewPort</code> used to project the
583
         * graphical information, and display it in the available area
584
         *
585
         * @see MapContext
586
         *
587
         * @see MapControl#setMapContext(MapContext)
588
         */
589
        public MapContext getMapContext() {
590
                return mapContext;
591
        }
592

    
593
        /**
594
         * <p>Registers a new behavior to this component.</p>
595
         *
596
         * <p>According the nature of the {@link Behavior Behavior}, different events will be generated. Those
597
         *  events can be caught by a particular {@link ToolListener ToolListener}, allowing user to interact with this
598
         *  <code>MapControl</code> object as a <i>tool</i>.</p>
599
         *
600
         * @param name name to identify the behavior to add
601
         * @param tool the behavior to add
602
         *
603
         * @see #addBehavior(String, Behavior[])
604
         * @see #getNamesMapTools()
605
         * @see #getMapToolsKeySet()
606
         * @see #hasTool(String)
607
         */
608
        public void addBehavior(String name, Behavior tool) {
609
                namesMapTools.put(name, tool);
610
                tool.setMapControl(this);
611
        }
612

    
613
        /**
614
         * <p>Registers a new behavior to this component as a {@link CompoundBehavior CompoundBehavior} made up of <code>tools</code>.</p>
615
         *
616
         * <p>According the nature of the behaviors registered, different events will be generated. Those
617
         *  events can be caught by a particular {@link ToolListener ToolListener}, allowing user to interact with this
618
         *  <code>MapControl</code> object as a <i>tool</i>.</p>
619
         *
620
         * @param name name to identify the compound behavior to add
621
         * @param tools the compound behavior to add
622
         *
623
         * @see #addBehavior(String, Behavior)
624
         * @see #getNamesMapTools()
625
         * @see #getMapToolsKeySet()
626
         * @see #hasTool(String)
627
         */
628
        public void addBehavior(String name, Behavior[] tools){
629
                CompoundBehavior tool = new CompoundBehavior(tools);
630
                addBehavior(name, tool);
631
        }
632

    
633
        /**
634
         * <p>Gets the <code>Behavior</code> registered in this component, identified
635
         *  by <code>name</code>.</p>
636
         *
637
         * @param name name of a registered behavior
638
         *
639
         * @return tool the registered behavior in this component as <code>name</code>, or <code>null</code> if
640
         *  no one has that identifier
641
         *
642
         * @see #addBehavior(String, Behavior)
643
         * @see #addBehavior(String, Behavior[])
644
         * @see #hasTool(String)
645
         */
646
        public Behavior getMapTool(String name) {
647
                return (Behavior)namesMapTools.get(name);
648
        }
649

    
650
        /**
651
         * <p>Returns a set view of the keys that identified the tools
652
         *  registered.</p>
653
         *
654
         * @return a set view of the keys that identified the tools registered
655
         *
656
         * @see HashMap#keySet()
657
         *
658
         * @see #getNamesMapTools()
659
         * @see #addBehavior(String, Behavior)
660
         * @see #addBehavior(String, Behavior[])
661
         */
662
        public Set getMapToolsKeySet() {
663
                return namesMapTools.keySet();
664
        }
665

    
666
        /**
667
         * <p>Returns <code>true</code> if this component contains a tool identified by <code>toolName</code>.</p>
668
         *
669
         * @param toolName identifier of the tool
670
         *
671
         * @return <code>true</code> if this component contains a tool identified by <code>toolName</code>; otherwise <code>false</code>
672
         *
673
         * @see #addBehavior(String, Behavior)
674
         * @see #addBehavior(String, Behavior[])
675
         */
676
        public boolean hasTool(String toolName) {
677
                return namesMapTools.containsKey(toolName);
678
        }
679

    
680
        /**
681
         * <p>Sets as current active <code>Behavior</code> associated to this component, that one which
682
         *  is registered and identified by <code>toolName</code>.</p>
683
         *
684
         * <p>Changing the current active behavior for this <code>MapControl</code>, implies also updating the
685
         *  previous <i>behavior</i> tool, and the current cursor.</p>
686
         *
687
         * @param toolName name of a registered behavior
688
         *
689
         * @see #getCurrentMapTool()
690
         * @see #getCurrentTool()
691
         */
692
        public void setTool(String toolName) {
693
                prevTool=getCurrentTool();
694
                Behavior mapTool = (Behavior) namesMapTools.get(toolName);
695
                currentMapTool = mapTool;
696
                currentTool = toolName;
697

    
698
                if (combinedTool != null) {
699
                        if (mapTool instanceof CompoundBehavior) {
700
                                ((CompoundBehavior)mapTool).addMapBehavior(combinedTool, true);
701
                        }
702
                        else {
703
                                currentMapTool = new CompoundBehavior(new Behavior[] {currentMapTool});
704
                                ((CompoundBehavior)currentMapTool).addMapBehavior(combinedTool, true);
705
                        }
706
                }
707

    
708
                //                this.setCursor(mapTool.getCursor());
709
        }
710

    
711
        /**
712
         * <p>Gets as current active <code>Behavior</code> associated to this component, that one which
713
         *  is registered and identified by <code>toolName</code>.</p>
714
         *
715
         * <p>Changing the current active behavior for this <code>MapControl</code>, implies also updating the
716
         *  previous <i>behavior</i> tool, and the current cursor.</p>
717
         *
718
         * @param toolName name of a registered behavior
719
         *
720
         * @see #getCurrentTool()
721
         * @see #setTool(String)
722
         */
723
        public Behavior getCurrentMapTool(){
724
                return currentMapTool;
725
        }
726

    
727
        /**
728
         * <p>Returns the name of the current selected tool on this MapControl</p>
729
         *
730
         * @return the name of the current's behavior tool associated to this component
731
         *
732
         * @see #getCurrentMapTool()
733
         * @see #setTool(String)
734
         */
735
        public String getCurrentTool() {
736
                return currentTool;
737
        }
738

    
739
        /**
740
         * <p>Determines that current drawing process of <code>MapControl</code>'s <code>MapContext</code>'s data must be canceled.</p>
741
         *
742
         * <p>It has no effects if now isn't drawing that graphical information.</p>
743
         *
744
         * <p>At last resort, the particular implementation of each layer in this <code>MapControl</code>'s <code>MapContrext</code>
745
         *   will be that one which will draw the graphical information, and, if supports, which could cancel its drawing subprocess.</p>
746
         */
747
        public void cancelDrawing() {
748
                /* if (drawer != null) {
749
                        if (!drawer.isAlive()) {
750
                                return;
751
                        }
752
                }
753
                 */
754
                canceldraw.setCanceled(true);
755

    
756
                /* while (!isCancelled) {
757
                        if (!drawer.isAlive()) {
758
                            // Si hemos llegado aqu? con un thread vivo, seguramente
759
                            // no estamos actualizados.
760

761
                                break;
762
                        }
763

764
                }
765
                canceldraw.setCancel(false);
766
                isCancelled = false;
767
        drawerAlive = false; */
768
        }
769

    
770
        /**
771
         * <p>Creates a {@link BufferedImage BufferedImage} image if there was no buffered image, or if
772
         *  its viewport's image height or width is different from this component's size. Once has created
773
         *  a double-buffer, fills it with the vieport's background color, or with <i>white</i> if it had no background color.</p>
774
         *
775
         * <p>If no double-buffered existed, creates a {@link BufferedImage BufferedImage} with the size of this component,
776
         * and as an image with 8-bit RGBA color components packed into integer pixels. That image has a <code>DirectColorModel</code> with alpha.
777
         * The color data in that image is considered not to be premultiplied with alpha.</p>
778
         *
779
         * <p>Once has created and filled the new inner <code>MapControl</code>'s double-buffer, changes the status to
780
         * <code>OUTDATED</code>.</p>
781
         *
782
         * @return <code>true</code> if has created and filled a new double-buffer for this <code>MapControl</code> instance; otherwise <code>false</code>
783
         */
784
        private boolean adaptToImageSize()
785
        {
786
                if ((image == null) || (vp.getImageWidth() != this.getWidth()) || (vp.getImageHeight() != this.getHeight()))
787
                {
788
                        image = new BufferedImage(this.getWidth(), this.getHeight(),
789
                                        BufferedImage.TYPE_INT_ARGB);
790
                        // ESTILO MAC
791
                        //                image = GraphicsEnvironment.getLocalGraphicsEnvironment()
792
                        //                                .getDefaultScreenDevice().getDefaultConfiguration()
793
                        //                                .createCompatibleImage(this.getWidth(), this.getHeight());
794
                        vp.setImageSize(new Dimension(getWidth(), getHeight()));
795
                        getMapContext().getViewPort().refreshExtent();
796

    
797

    
798
                        Graphics gTemp = image.createGraphics();
799
                        Color theBackColor = vp.getBackColor();
800
                        if (theBackColor == null) {
801
                                gTemp.setColor(Color.WHITE);
802
                        } else {
803
                                gTemp.setColor(theBackColor);
804
                        }
805

    
806
                        gTemp.fillRect(0,0,getWidth(), getHeight());
807
                        gTemp.dispose();
808
                        status = DESACTUALIZADO;
809
                        // g.drawImage(image,0,0,null);
810
                        return true;
811
                }
812
                return false;
813
        }
814

    
815
        /**
816
         * <p>Paints the graphical information of this component using a double buffer.</p>
817
         *
818
         * <p>If the double buffer wasn't created, creates a new one.</p>
819
         *
820
         * <p>Paints the component according the following algorithm:
821
         * <br>
822
         *  &nbsp If <i>status</i> is <i>UPDATED</i>:<br>
823
         *  &nbsp &nbsp If there is no <i>double buffer</i>:<br>
824
         *  &nbsp &nbsp &nbsp If there is a <i>behavior</i> for managing the <code>MapControl</code> instance, delegates
825
         *   the drawing process to that behavior, calling: <code><i>behavior_instance</i>.paintComponent(g)</code> &nbsp .<br>
826
         *  &nbsp &nbsp &nbsp Else, repaints the current graphical information quickly calling: <code>g.drawImage(image,0,0,null)</code> &nbsp .<br>
827
         *  &nbsp Else, (<i>status</i> is <i>OUTDATED</i>, or <i>ONLY_GRAPHICS</i>): executes a quickly repaint of the previous information calling <code>g.drawImage(image,0,0,null)</code>, and creates
828
         *   a <i>painting request</i> to delegate the heavy drawing process to the {@link Drawer2 Drawer2}'s worker thread, according the <i>SingleWorketThread</i> pattern, starting a timer to update
829
         *   (invoking <code>repaint()</code> that comprises invoke this method) the view every delay of 360 ms. during the the process drawing.</p>
830
         *
831
         * @see javax.swing.JComponent#paintComponent(java.awt.Graphics)
832
         * @see Drawer2
833
         */
834
        protected void paintComponent(Graphics g) {
835
                adaptToImageSize();
836

    
837
                mapControlDrawer.setGraphics(g);
838
                mapControlDrawer.setViewPort(getMapContext().getViewPort());
839

    
840
                if (status == ACTUALIZADO) {
841
                        /*
842
                         * Si hay un behaviour y la imagen es distinta de null se delega el dibujado
843
                         * en dicho behaviour
844
                         */
845
                        if (image != null)
846
                        {
847
                                if (currentMapTool != null) {
848
                                        currentMapTool.paintComponent(mapControlDrawer);
849
                                } else {
850
                                        mapControlDrawer.drawImage(image,0,0);
851
                                }                                
852
                        }
853
                } else if ((status == DESACTUALIZADO)
854
                                || (status == ONLY_GRAPHICS)) {
855

    
856
                        mapControlDrawer.drawImage(image,0,0);
857

    
858
                        drawer.put(new PaintingRequest());
859
                        timer.start();                        
860
                }
861
                cadgrid.drawGrid(mapControlDrawer);
862
                drawCursor();
863
        }
864

    
865
        /**
866
         * <p>Gets the {@link BufferedImage BufferedImage} used to accelerate the draw of new ''frames'' with changes,
867
         * or new graphical items in this component.</p>
868
         *
869
         * @return double buffered image used by this component to accelerate the draw of its graphical information, or
870
         * <code>null</code> if isn't already created
871
         *
872
         * @see BufferedImage
873
         */
874
        public BufferedImage getImage() {
875
                return image;
876
        }
877

    
878
        /**
879
         * <p>Forces repaint all visible graphical information in this component.</p>
880
         *
881
         * <p>If <code>doClear == true</code>, before repainting, clears the background color, with the
882
         *  inner viewport's background color.</p>
883
         *
884
         * @param doClear <code>true</code> if needs clearing the background color before drawing the map
885
         *
886
         * @see #cancelDrawing()
887
         * @see FLayers#setDirty(boolean)
888
         */
889
        public void drawMap(boolean doClear) {
890
                cancelDrawing();
891
                //System.out.println("drawMap con doClear=" + doClear);
892
                status = DESACTUALIZADO;
893
                if (doClear)
894
                {
895
                        // image = null; // Se usa para el PAN
896
                        if (image != null)
897
                        {
898
                                Graphics2D g = image.createGraphics();
899
                                Color theBackColor = vp.getBackColor();
900
                                if (theBackColor == null) {
901
                                        g.setColor(Color.WHITE);
902
                                } else {
903
                                        g.setColor(theBackColor);
904
                                }
905
                                g.fillRect(0, 0, vp.getImageWidth(), vp.getImageHeight());
906
                                g.dispose();
907
                        }
908
                }
909
                repaint();
910
        }
911

    
912

    
913
        /**
914
         * <p>Cancels any current drawing process, changing the status to <code>OUTDATED</code>, and forcing
915
         * repaint only the layers dirty.</p>
916
         *
917
         * @see #cancelDrawing()
918
         */
919
        public void rePaintDirtyLayers()
920
        {
921
                cancelDrawing();
922
                status = DESACTUALIZADO;
923
                repaint();
924
        }
925

    
926
        /**
927
         * <p>Cancels any current drawing process, changing the status to <code>ONLY_GRAPHICS</code>, and forcing
928
         * repaint only the graphical layer of the <code>MapContext</code>.</p>
929
         */
930
        public void drawGraphics() {
931
                status = ONLY_GRAPHICS;
932
                repaint();
933
        }
934

    
935
        /**
936
         * @see java.awt.event.ComponentListener#componentHidden(java.awt.event.ComponentEvent)
937
         */
938
        public void componentHidden(ComponentEvent e) {
939
        }
940

    
941
        /**
942
         * @see java.awt.event.ComponentListener#componentMoved(java.awt.event.ComponentEvent)
943
         */
944
        public void componentMoved(ComponentEvent e) {
945
        }
946

    
947
        /**
948
         * @see java.awt.event.ComponentListener#componentResized(java.awt.event.ComponentEvent)
949
         */
950
        public void componentResized(ComponentEvent e) {
951
                /* image = new BufferedImage(this.getWidth(), this.getHeight(),
952
                                BufferedImage.TYPE_INT_ARGB);
953
                Graphics gTemp = image.createGraphics();
954
                gTemp.setColor(vp.getBackColor());
955
                gTemp.fillRect(0,0,getWidth(), getHeight());
956
        System.out.println("MapControl resized");
957
            // image = null;
958
            vp.setImageSize(new Dimension(getWidth(), getHeight()));
959
                getMapContext().getViewPort().setScale(); */
960
                // drawMap(true);
961
        }
962

    
963
        /**
964
         * @see java.awt.event.ComponentListener#componentShown(java.awt.event.ComponentEvent)
965
         */
966
        public void componentShown(ComponentEvent e) {
967
        }
968

    
969
        /**
970
         * @see ExceptionHandlingSupport#addExceptionListener(ExceptionListener)
971
         */
972
        public void addExceptionListener(ExceptionListener o) {
973
                exceptionHandlingSupport.addExceptionListener(o);
974
        }
975

    
976
        /**
977
         * @see ExceptionHandlingSupport#removeExceptionListener(ExceptionListener)
978
         */
979
        public boolean removeExceptionListener(ExceptionListener o) {
980
                return exceptionHandlingSupport.removeExceptionListener(o);
981
        }
982

    
983
        /**
984
         * @see ExceptionHandlingSupport#throwException(Throwable)
985
         */
986
        protected void throwException(Throwable t) {
987
                exceptionHandlingSupport.throwException(t);
988
        }
989

    
990
        /**
991
         * <p>Represents each <code>MapControl</code>'s data painting request.</p>
992
         *
993
         * <p>The request will be attended by a <code>Drawer2</code>, which will hold it since the <code>Drawer2</code>'s worker
994
         *  takes it, or arrives a new painting request, which will replace it.</p>
995
         */
996
        private class PaintingRequest
997
        {
998
                /**
999
                 * <p>Creates a new <code>PaintingRequest</p> instance.</p>
1000
                 */
1001
                public PaintingRequest()
1002
                {
1003
                }
1004

    
1005
                /**
1006
                 * <p><code>MapControl</code> paint process:</p>
1007
                 *
1008
                 * <p>
1009
                 *  <ul>
1010
                 *   <li><i>1.- </i>Cancels all previous <code>MapControl</code>'s drawing processes.</li>
1011
                 *   <li><i>2.- </i>If <i>status</i> was OUTDATED:
1012
                 *    <ul>
1013
                 *     <li><i>2.1.- </i>Fills the background color with viewport's background color, or <i>white</i> if it was undefined.</li>
1014
                 *     <li><i>2.2.- </i>Notifies <i>MapContext</i> to be drawn invoking: <code>mapContext.draw(double-buffer, double-buffer's buffer, shared cancel-draw object, mapContext.getScaleView());</code>.</li>
1015
                 *     <li><i>2.3.- </i>If <code>canceldraw.isCanceled()</code>
1016
                 *      <ul>
1017
                 *       <li><i>2.3.1.- </i>Sets <i>status</i> to OUTDATED.</li>
1018
                 *       <li><i>2.3.2.- </i>Sets <i>dirty</i> all layers stored in <i>MapContext</i>.</li>
1019
                 *      </ul>
1020
                 *     </li>
1021
                 *     <li><i>2.4.- </i>Else, sets <i>status</i> to UPDATED.</li>
1022
                 *    </ul>
1023
                 *   </li>
1024
                 *   <li><i>3.- </i>Else, if <i>status</i> was ONLY_GRAPHICS:
1025
                 *    <ul>
1026
                 *     <li><i>3.1.- </i>Sets <i>status</i> to UPDATED.</li>
1027
                 *     <li><i>3.2.- </i>Notifies <i>MapContext</i> to be drawn invoking: <code>mapContext.drawGraphics(double-buffer, double-buffer's buffer, shared cancel-draw object, mapContext.getScaleView());</code>.</li>
1028
                 *    </ul>
1029
                 *   </li>
1030
                 *   <li><i>4.- </i>Stops the <i>timer</i>.</li>
1031
                 *   <li><i>5.- </i>Repaints this component invoking: <code>repaint();</code></li>
1032
                 *  </ul>
1033
                 * </p>
1034
                 *
1035
                 * @see #cancelDrawing()
1036
                 * @see MapContext#draw(BufferedImage, Graphics2D, Cancellable, double)
1037
                 * @see MapContext#drawGraphics(BufferedImage, Graphics2D, Cancellable, double)
1038
                 *
1039
                 * @see ViewPort
1040
                 */
1041
                public void paint()
1042
                {
1043
                        try
1044
                        {
1045
                                canceldraw.setCanceled(false);
1046
                                /* if (image == null)
1047
                {
1048
                    image = new BufferedImage(vp.getImageWidth(), vp.getImageHeight(),
1049
                            BufferedImage.TYPE_INT_ARGB);
1050
                    Graphics gTemp = image.createGraphics();
1051
                    Color theBackColor = vp.getBackColor();
1052
                    if (theBackColor == null)
1053
                        gTemp.setColor(Color.WHITE);
1054
                    else
1055
                        gTemp.setColor(theBackColor);
1056

1057
                    gTemp.fillRect(0,0,getWidth(), getHeight());
1058
                    gTemp.dispose();
1059
                    // g.drawImage(image,0,0,null);
1060
                    System.out.println("Imagen con null en DESACTUALIZADO. Width = " + this.getWidth());
1061
                } */
1062
                                Graphics2D g = image.createGraphics();
1063

    
1064
                                ViewPort viewPort = mapContext.getViewPort();
1065

    
1066
                                if (status == DESACTUALIZADO)
1067
                                {
1068
                                        Graphics2D gTemp = image.createGraphics();
1069
                                        Color theBackColor = viewPort.getBackColor();
1070
                                        if (theBackColor == null) {
1071
                                                gTemp.setColor(Color.WHITE);
1072
                                        } else {
1073
                                                gTemp.setColor(theBackColor);
1074
                                        }
1075
                                        gTemp.fillRect(0, 0, viewPort.getImageWidth(), viewPort.getImageHeight());
1076
                                        // ESTILO MAC
1077
                                        //                  BufferedImage imgMac = new BufferedImage(vp.getImageWidth(), vp.getImageHeight(),
1078
                                        //                          BufferedImage.TYPE_INT_ARGB);
1079
                                        //
1080
                                        //                  mapContext.draw(imgMac, g, canceldraw, mapContext.getScaleView());
1081
                                        //                  g.drawImage(imgMac, 0, 0, null);
1082
                                        // FIN ESTILO MAC
1083
                                        // SIN MAC:
1084

    
1085
                                        mapContext.draw(image, g, canceldraw, mapContext.getScaleView());
1086
                                        if (!canceldraw.isCanceled()){
1087
                                                status=ACTUALIZADO;
1088
                                        }else{
1089
                                                status=DESACTUALIZADO;
1090
                                                //                          getMapContext().getLayers().setDirty(true);
1091
                                        }
1092

    
1093
                                }
1094
                                else if (status == ONLY_GRAPHICS)
1095
                                {
1096
                                        status = ACTUALIZADO;
1097
                                        mapContext.drawGraphics(image, g, canceldraw,mapContext.getScaleView());
1098

    
1099
                                }
1100

    
1101

    
1102

    
1103
                                // status = FAST_PAINT;
1104
                                //  drawerAlive = false;
1105
                                timer.stop();
1106
                                repaint();
1107

    
1108

    
1109
                        } catch (Throwable e) {
1110
                                timer.stop();
1111
                                //  isCancelled = true;
1112
                                e.printStackTrace();
1113
                                throwException(e);
1114
                        } finally {
1115
                        }
1116
                }
1117
        }
1118

    
1119
        /**
1120
         * <p>An instance of <code>Drawer2</code> could manage all <code>MapControl</code> painting requests.</p>
1121
         *
1122
         * <p>Based on the <i>WorkerThread</i> software pattern, creates a worker thread that will attend sequentially
1123
         *  the current waiting painting request, after finishing the previous (that could be by a cancel action).</p>
1124
         *
1125
         * <p>All new {@link PaintingRequest PaintingRequest} generated will be stored as <i>waiting requests</i> since the worker
1126
         * attends it.</p>
1127
         *
1128
         * <p>If a worker finished and there was no <i>painting request</i>, the worker would be set to wait until any
1129
         *  <i>painting request</i> would be put.</p>
1130
         *
1131
         * @author fjp
1132
         */
1133
        public class Drawer
1134
        {
1135
                // Una mini cola de 2. No acumulamos peticiones de dibujado
1136
                // dibujamos solo lo ?ltimo que nos han pedido.
1137

    
1138

    
1139
                /**
1140
                 * <p>Painting request that's been attended by the <code>Drawer2</code>'s worker.</p>
1141
                 *
1142
                 * @see #put(org.gvsig.fmap.mapcontrol.MapControl.PaintingRequest)
1143
                 * @see #take()
1144
                 */
1145
                private PaintingRequest paintingRequest;
1146

    
1147
                /**
1148
                 * <p>Painting request waiting to be attended by the <code>Drawer2</code>'s worker.</p>
1149
                 *
1150
                 * @see #put(org.gvsig.fmap.mapcontrol.MapControl.PaintingRequest)
1151
                 * @see #take()
1152
                 */
1153
                private PaintingRequest waitingRequest;
1154

    
1155
                /**
1156
                 * <p>Determines that the <code>Drawer2</code>'s worker is busy attending a painting request.</p>
1157
                 *
1158
                 * @see #put(org.gvsig.fmap.mapcontrol.MapControl.PaintingRequest)
1159
                 * @see #take()
1160
                 */
1161
                private boolean waiting;
1162

    
1163
                /**
1164
                 * <p>Notifies the <code>Drawer2</code>'s worker to finish or continue with its process.</p>
1165
                 *
1166
                 * @see #setShutdown(boolean)
1167
                 */
1168
                private boolean shutdown;
1169

    
1170
                /**
1171
                 * <p>Sets this <code>Drawer2</code>'s worker to finish or continue with its process.</p>
1172
                 *
1173
                 * @param isShutdown a boolean value
1174
                 */
1175
                public void setShutdown(boolean isShutdown)
1176
                {
1177
                        shutdown = isShutdown;
1178
                }
1179

    
1180
                /**
1181
                 * <p>Creates a new drawer for managing all data painting requests in <code>MapControl</code>.</p>
1182
                 *
1183
                 * <p>Includes the following steps:
1184
                 *  <ul>
1185
                 *   <li>By default, there is no <i>current painting request</i>.</li>
1186
                 *   <li>By default, there is no <i>waiting painting request</i>.</li>
1187
                 *   <li>By default, the worker thread is waiting no <i>painting request</i>.</li>
1188
                 *   <li>By default, the worker thread is running.</li>
1189
                 *   <li>Creates and starts a worker thread for attending the <i>painting requests</i>.</li>
1190
                 *  </ul>
1191
                 * </p>
1192
                 */
1193
                public Drawer()
1194
                {
1195
                        paintingRequest = null;
1196
                        waitingRequest = null;
1197
                        waiting = false;
1198
                        shutdown = false;
1199
                        new Thread(new Worker()).start();
1200
                }
1201

    
1202
                /**
1203
                 * <p>Sets a <code>PaintingRequest</code> to be attended by the worker thread of this object. If
1204
                 *  this one was waiting, wakes up.</p>
1205
                 *
1206
                 * <p>All waiting threads will be notified synchronized.</p>
1207
                 *
1208
                 * @param newPaintRequest
1209
                 *
1210
                 * @see #take()
1211
                 */
1212
                public void put(PaintingRequest newPaintRequest)
1213
                {
1214
                        waitingRequest = newPaintRequest;
1215
                        if (waiting)
1216
                        {
1217
                                synchronized (this) {
1218
                                        notifyAll();
1219
                                }
1220
                        }
1221
                }
1222

    
1223
                /**
1224
                 * <p>Used by this object's worker, returns the current waiting drawing request, causing current thread
1225
                 *  to wait until another thread invokes {@link #put(org.gvsig.fmap.mapcontrol.MapControl.PaintingRequest) #put(com.iver.cit.gvsig.fmap.MapControl.PaintingRequest)},
1226
                 *  if there was no waiting request.</p>
1227
                 *
1228
                 * <p>All threads will access synchronized to the waiting request.</p>
1229
                 *
1230
                 * @return <code>PaintingRequest</code> that was waiting to be attended
1231
                 *
1232
                 * @see #put(org.gvsig.fmap.mapcontrol.MapControl.PaintingRequest)
1233
                 */
1234
                public PaintingRequest take()
1235
                {
1236
                        if (waitingRequest == null)
1237
                        {
1238
                                synchronized (this) {
1239
                                        waiting = true;
1240
                                        try {
1241
                                                wait();
1242
                                        }
1243
                                        catch (InterruptedException ie)
1244
                                        {
1245
                                                waiting = false;
1246
                                        }
1247
                                }
1248
                        }
1249
                        paintingRequest = waitingRequest;
1250
                        waitingRequest = null;
1251
                        return paintingRequest;
1252
                }
1253

    
1254
                /**
1255
                 * <p>Thread for attending painting requests.</p>
1256
                 *
1257
                 * <p>If there was no double buffer, sets the status to <code>OUTDATED</code> and finishes, otherwise
1258
                 *  takes the painting request (it's probably that would wait some time), cancel the previous drawing
1259
                 *  process, and starts processing the request.</p>
1260
                 *
1261
                 * @see Thread
1262
                 */
1263
                private class Worker implements Runnable
1264
                {
1265
                        /*
1266
                         * (non-Javadoc)
1267
                         * @see java.lang.Runnable#run()
1268
                         */
1269
                        public void run()
1270
                        {
1271
                                while (!shutdown)
1272
                                {
1273
                                        PaintingRequest p = take();
1274
                                        //System.out.println("Pintando");
1275
                                        if (image != null){
1276
                                                cancelDrawing();
1277
                                                p.paint();
1278
                                        } else{
1279
                                                status = DESACTUALIZADO;
1280
                                        }
1281
                                }
1282
                        }
1283
                }
1284
        }
1285

    
1286
        /**
1287
         * <p>An instance of <code>CancelDraw</code> will be shared by all this <code>MapControl</code>'s <code>MapContext</code> layers,
1288
         *  allowing receive a notification that, when they're been drawn, to be cancelled.</p>
1289
         *
1290
         * @see Cancellable
1291
         *
1292
         * @author Fernando Gonz?lez Cort?s
1293
         */
1294
        public class CancelDraw implements Cancellable {
1295
                /**
1296
                 * <p>Determines if the drawing task must be canceled or not.</p>
1297
                 *
1298
                 * @see #isCanceled()
1299
                 * @see #setCanceled(boolean)
1300
                 */
1301
                private boolean cancel = false;
1302

    
1303
                /**
1304
                 * Creates a new <code>CancelDraw</code> object.
1305
                 */
1306
                public CancelDraw() {
1307
                }
1308

    
1309
                /*
1310
                 * (non-Javadoc)
1311
                 * @see com.iver.utiles.swing.threads.Cancellable#setCanceled(boolean)
1312
                 */
1313
                public void setCanceled(boolean b) {
1314
                        cancel = b;
1315
                }
1316

    
1317
                /*
1318
                 * (non-Javadoc)
1319
                 * @see com.iver.utiles.swing.threads.Cancellable#isCanceled()
1320
                 */
1321
                public boolean isCanceled() {
1322
                        return cancel;
1323
                }
1324
        }
1325

    
1326
        /**
1327
         * <p>Listens all kind of mouse events produced in {@link MapControl MapControl}, and invokes its current
1328
         *  map tool <i>({@link MapControl#getCurrentMapTool() MapControl#getCurrentMapTool()}</i> to simulate a behavior.</p>
1329
         *
1330
         * <p>Mouse wheel moved events produce a <i>zoom in</i> operation if wheel rotation is negative, or a <i>zoom out</i>
1331
         *  if its positive. Both will be centered in the position of the mouse, but, meanwhile <i>zoom in</i> operation
1332
         *  applies a factor of 0.9, <i>zoom out</i> operation applies a factor of 1.2</p>
1333
         *
1334
         * <p>Mouse wheel moved events can be produced as much frequently, that between each one, the drawing process could
1335
         *  hadn't finished. This is the reason that, in this situation, cancels always the previous drawing process before
1336
         *  applying a <i>zoom</i> operation, and ignores all new mouse positions that are produced before 1 second.</p>
1337
         *
1338
         * @author Fernando Gonz?lez Cort?s
1339
         */
1340
        public class MapToolListener implements MouseListener, MouseWheelListener,
1341
        MouseMotionListener {
1342

    
1343
                /**
1344
                 * <p>Used to avoid mouse wheel move events closed.</p>
1345
                 *
1346
                 * <p>If a mouse wheel move event is produced
1347
                 */
1348
                long t1;
1349

    
1350
                /**
1351
                 * <p>Position of the mouse, in map coordinates.</p>
1352
                 *
1353
                 * <p>This point coordinates will be used as center of the <i>zoom</i> operation.</p>
1354
                 */
1355
                Point2D pReal;
1356

    
1357
                /**
1358
                 * @see java.awt.event.MouseListener#mouseClicked(java.awt.event.MouseEvent)
1359
                 * @see Behavior#mouseClicked(MouseEvent)
1360
                 */
1361
                public void mouseClicked(MouseEvent e) {
1362
                        try {
1363
                                if (currentMapTool != null) {
1364
                                        currentMapTool.mouseClicked(e);
1365
                                }
1366
                        } catch (BehaviorException t) {
1367
                                throwException(t);
1368
                        }
1369
                }
1370

    
1371
                /**
1372
                 * @see java.awt.event.MouseListener#mouseEntered(java.awt.event.MouseEvent)
1373
                 * @see Behavior#mouseEntered(MouseEvent)
1374
                 */
1375
                public void mouseEntered(MouseEvent e) {
1376
                        setToolMouse();
1377
                        try {
1378
                                if (currentMapTool != null) {
1379
                                        currentMapTool.mouseEntered(e);
1380
                                }
1381
                        } catch (BehaviorException t) {
1382
                                throwException(t);
1383
                        }
1384
                }
1385

    
1386
                /**
1387
                 * @see java.awt.event.MouseListener#mouseExited(java.awt.event.MouseEvent)
1388
                 * @see Behavior#mouseExited(MouseEvent)
1389
                 */
1390
                public void mouseExited(MouseEvent e) {
1391
                        try {
1392
                                if (currentMapTool != null) {
1393
                                        currentMapTool.mouseExited(e);
1394
                                }
1395
                        } catch (BehaviorException t) {
1396
                                throwException(t);
1397
                        }
1398
                        //Remove the snapping image if exist
1399
                        usedSnap = null;
1400
                        repaint();
1401
                }
1402

    
1403
                /**
1404
                 * @see java.awt.event.MouseListener#mousePressed(java.awt.event.MouseEvent)
1405
                 * @see Behavior#mousePressed(MouseEvent)
1406
                 */
1407
                public void mousePressed(MouseEvent e) {
1408
                        try {
1409
                                if (currentMapTool != null) {
1410
                                        currentMapTool.mousePressed(e);
1411
                                }
1412
                        } catch (BehaviorException t) {
1413
                                throwException(t);
1414
                        }
1415
                }
1416

    
1417
                /**
1418
                 * @see java.awt.event.MouseListener#mouseReleased(java.awt.event.MouseEvent)
1419
                 * @see Behavior#mouseReleased(MouseEvent)
1420
                 */
1421
                public void mouseReleased(MouseEvent e) {
1422
                        try {
1423
                                if (currentMapTool != null) {
1424
                                        currentMapTool.mouseReleased(e);
1425
                                }
1426
                        } catch (BehaviorException t) {
1427
                                throwException(t);
1428
                        }
1429
                }
1430

    
1431
                /**
1432
                 * @see java.awt.event.MouseWheelListener#mouseWheelMoved(java.awt.event.MouseWheelEvent)
1433
                 * @see Behavior#mouseWheelMoved(MouseWheelEvent)
1434
                 */
1435
                public void mouseWheelMoved(MouseWheelEvent e) {
1436
                        try {
1437
                                if (currentMapTool == null) {
1438
                                        return;
1439
                                }
1440

    
1441
                                currentMapTool.mouseWheelMoved(e);
1442

    
1443
                                // Si el tool actual no ha consumido el evento
1444
                                // entendemos que quiere el comportamiento por defecto.
1445
                                if (!e.isConsumed())
1446
                                {
1447
                                        // Para usar el primer punto sobre el que queremos centrar
1448
                                        // el mapa, dejamos pasar un segundo para considerar el siguiente
1449
                                        // punto como v?lido.
1450
                                        if (t1 == 0)
1451
                                        {
1452
                                                t1= System.currentTimeMillis();
1453
                                                pReal = vp.toMapPoint(e.getPoint());
1454
                                        }
1455
                                        else
1456
                                        {
1457
                                                long t2 = System.currentTimeMillis();
1458
                                                if ((t2-t1) > 1000) {
1459
                                                        t1=0;
1460
                                                }
1461
                                        }
1462
                                        cancelDrawing();
1463
                                        ViewPort vp = getViewPort();
1464

    
1465

    
1466
                                        /* Point2D pReal = new Point2D.Double(vp.getAdjustedExtent().getCenterX(),
1467
                                                        vp.getAdjustedExtent().getCenterY()); */
1468
                                        int amount = e.getWheelRotation();
1469
                                        double nuevoX;
1470
                                        double nuevoY;
1471
                                        double factor;
1472

    
1473
                                        if (amount < 0) // nos acercamos
1474
                                        {
1475
                                                factor = 0.9;
1476
                                        } else // nos alejamos
1477
                                        {
1478
                                                factor = 1.2;
1479
                                        }
1480
                                        if (vp.getExtent() != null) {
1481
                                                nuevoX = pReal.getX()
1482
                                                - ((vp.getExtent().getWidth() * factor) / 2.0);
1483
                                                nuevoY = pReal.getY()
1484
                                                - ((vp.getExtent().getHeight() * factor) / 2.0);
1485
                                                double x = nuevoX;
1486
                                                double y = nuevoY;
1487
                                                double width = vp.getExtent().getWidth() * factor;
1488
                                                double height = vp.getExtent().getHeight() * factor;
1489

    
1490
                                                try {
1491
                                                        vp.setEnvelope(geomManager.createEnvelope(x,y,x+width,y+height, SUBTYPES.GEOM2D));
1492
                                                } catch (CreateEnvelopeException e1) {
1493
                                                        logger.error("Error creating the envelope", e);
1494
                                                }
1495
                                        }
1496

    
1497

    
1498
                                }
1499
                        } catch (BehaviorException t) {
1500
                                throwException(t);
1501
                        }
1502
                }
1503

    
1504
                /**
1505
                 * @see java.awt.event.MouseMotionListener#mouseDragged(java.awt.event.MouseEvent)
1506
                 * @see Behavior#mouseDragged(MouseEvent)
1507
                 */
1508
                public void mouseDragged(MouseEvent e) {
1509
                        calculateSnapPoint(e.getPoint());
1510
                        try {
1511
                                if (currentMapTool != null) {
1512
                                        currentMapTool.mouseDragged(e);
1513
                                }
1514
                        } catch (BehaviorException t) {
1515
                                throwException(t);
1516
                        }
1517
                        repaint();
1518
                }
1519

    
1520
                /**
1521
                 * @see java.awt.event.MouseMotionListener#mouseMoved(java.awt.event.MouseEvent)
1522
                 * @see Behavior#mouseMoved(MouseEvent)
1523
                 */
1524
                public void mouseMoved(MouseEvent e) {
1525
                        calculateSnapPoint(e.getPoint());
1526
                        try {
1527
                                if (currentMapTool != null) {
1528
                                        currentMapTool.mouseMoved(e);
1529
                                }
1530
                        } catch (BehaviorException t) {
1531
                                throwException(t);
1532
                        }
1533
                        repaint();
1534
                }
1535
        }
1536

    
1537
        /**
1538
         * <p<code>MapContextListener</code> listens all events produced in a <code>MapControl</code>'s <code>MapContext</code>
1539
         * object during an atomic period of time, and sets it to dirty, <i>executing <code>drawMap(false)</code>, if any of the
1540
         * following conditions is accomplished</i>:
1541
         * <ul>
1542
         *  <li>Any of the <code>LayerEvent</code> in the <code>AtomicEvent</code> parameter notifies a <i>visibility change</i>.</li>
1543
         *  <li>There is at least one <code>ColorEvent</code> in the <code>AtomicEvent</code> parameter.</li>
1544
         *  <li>There is at least one <code>ExtentEvent</code> in the <code>AtomicEvent</code> parameter.</li>
1545
         *  <li>Any of the <code>LayerCollectionEvent</code> in the <code>AtomicEvent</code> parameter notifies that a driver's layer has reloaded it successfully.</li>
1546
         *  <li>There is at least one <code>LegendEvent</code> in the <code>AtomicEvent</code> parameter.</li>
1547
         *  <li>There is at least one <code>SelectionEvent</code> in the <code>AtomicEvent</code> parameter.</li>
1548
         * </ul>
1549
         * </p>
1550
         *
1551
         * @author Fernando Gonz?lez Cort?s
1552
         */
1553
        public class MapContextListener implements AtomicEventListener {
1554
                /**
1555
                 * @see org.gvsig.fmap.mapcontext.events.listeners.AtomicEventListener#atomicEvent(org.gvsig.fmap.mapcontext.events.AtomicEvent)
1556
                 */
1557
                public void atomicEvent(AtomicEvent e) {
1558
                        LayerEvent[] layerEvents = e.getLayerEvents();
1559

    
1560
                        int eType;
1561
                        for (int i = 0; i < layerEvents.length; i++) {
1562
                                eType=layerEvents[i].getEventType();
1563
                                //                                if (eType == LayerEvent.DRAW_VALUES_CHANGED || eType == LayerEvent.VISIBILITY_CHANGED) {
1564
                                //                                        MapControl.this.drawMap(false);
1565
                                //                                        return;
1566
                                //                                }
1567
                                if (layerEvents[i].getProperty().equals("visible")) {
1568
                                        MapControl.this.drawMap(false);
1569
                                        return;
1570
                                } else if (layerEvents[i].getEventType() == LayerEvent.EDITION_CHANGED){
1571
                                        MapControl.this.drawMap(false);
1572
                                        return;
1573
                                }
1574
                        }
1575

    
1576
                        if (e.getColorEvents().length > 0) {
1577
                                MapControl.this.drawMap(false);
1578
                                return;
1579
                        }
1580

    
1581
                        if (e.getExtentEvents().length > 0) {
1582
                                MapControl.this.drawMap(false);
1583
                                return;
1584
                        }
1585

    
1586
                        if (e.getProjectionEvents().length > 0) {
1587
                                //redraw = true;
1588
                        }
1589

    
1590

    
1591
                        LayerCollectionEvent[] aux = e.getLayerCollectionEvents();
1592
                        if (aux.length > 0) {
1593
                                for (int i=0; i < aux.length; i++)
1594
                                {
1595
                                        if (aux[i].getAffectedLayer().getFLayerStatus().isDriverLoaded())
1596
                                        {
1597
                                                MapControl.this.drawMap(false);
1598
                                                return;
1599
                                        }
1600
                                }
1601

    
1602
                        }
1603

    
1604
                        if (e.getLegendEvents().length > 0) {
1605
                                MapControl.this.drawMap(false);
1606
                                return;
1607
                        }
1608

    
1609
                        if (e.getSelectionEvents().length > 0) {
1610
                                MapControl.this.drawMap(false);
1611
                                return;
1612
                        }
1613
                }
1614
        }
1615

    
1616
        /**
1617
         * <p>Gets the <code>ViewPort</code> of this component's {@link MapContext MapContext} .</p>
1618
         *
1619
         * @see MapContext#getViewPort()
1620
         */
1621
        public ViewPort getViewPort() {
1622
                return vp;
1623
        }
1624

    
1625
        /**
1626
         * <p>Returns all registered <code>Behavior</code> that can define a way to work with this <code>MapControl</code>.</p>
1627
         *
1628
         * @return registered <code>Behavior</code> to this <code>MapControl</code>
1629
         *
1630
         * @see #addBehavior(String, Behavior)
1631
         * @see #addBehavior(String, Behavior[])
1632
         * @see #getMapToolsKeySet()
1633
         * @see #hasTool(String)
1634
         */
1635
        public HashMap getNamesMapTools() {
1636
                return namesMapTools;
1637
        }
1638

    
1639
        /*
1640
         * (non-Javadoc)
1641
         * @see com.iver.cit.gvsig.fmap.edition.commands.CommandListener#commandRepaint()
1642
         */
1643
        public void commandRepaint() {
1644
                drawMap(false);
1645
        }
1646

    
1647
        /*
1648
         * (non-Javadoc)
1649
         * @see com.iver.cit.gvsig.fmap.edition.commands.CommandListener#commandRefresh()
1650
         */
1651
        public void commandRefresh() {
1652
                // TODO Auto-generated method stub
1653
        }
1654

    
1655
        /**
1656
         * <p>Equivalent operation to <i>undo</i>.</p>
1657
         *
1658
         * <p>Exchanges the previous tool with the current one.</p>
1659
         *
1660
         * @see #addBehavior(String, Behavior)
1661
         * @see #addBehavior(String, Behavior[])
1662
         * @see #setTool(String)
1663
         */
1664
        public void setPrevTool() {
1665
                setTool(prevTool);
1666
        }
1667

    
1668
        /**
1669
         * <p>Executes a <i>zoom in</i> operation centered at the center of the extent.</p>
1670
         *
1671
         * <p>This implementation is designed for being invoked outside a <code>Behavior</code>, for example
1672
         *  by an action of pressing a button; and simulates that the event has been produced by
1673
         *  releasing the <i>button 1</i> of the mouse using the registered <code>Behavior</code> in this
1674
         *  <code>MapControl</code> object that's responsible for the <i>zoom in</i> operation.</p>
1675
         *
1676
         * @see #zoomOut()
1677
         */
1678
        public void zoomIn() {
1679
                Behavior mapTool = (Behavior) namesMapTools.get("zoomIn");
1680
                ViewPort vp=getViewPort();
1681
                Envelope r=getViewPort().getAdjustedExtent();
1682
                Point2D pCenter=vp.fromMapPoint(r.getCenter(0),r.getCenter(1));
1683
                MouseEvent e=new MouseEvent(this,MouseEvent.MOUSE_RELEASED,MouseEvent.ACTION_EVENT_MASK,MouseEvent.BUTTON1,(int)pCenter.getX(),(int)pCenter.getY(),1,true,MouseEvent.BUTTON1);
1684
                try {
1685
                        mapTool.mousePressed(e);
1686
                        mapTool.mouseReleased(e);
1687
                } catch (BehaviorException t) {
1688
                        throwException(t);
1689
                }
1690
        }
1691

    
1692
        /**
1693
         * <p>Executes a <i>zoom out</i> operation centered at the center of the extent.</p>
1694
         *
1695
         * <p>This implementation is thought for being invoked outside a <code>Behavior</code>, for example
1696
         *  by an action of pressing a button, and simulates that the event has been produced by
1697
         *  releasing the <i>button 1</i> of the mouse using the registered <code>Behavior</code> in this
1698
         *  <code>MapControl</code> object that's responsible for the <i>zoom out</i> operation.</p>
1699
         *
1700
         * @see #zoomIn()
1701
         */
1702
        public void zoomOut() {
1703
                Behavior mapTool = (Behavior) namesMapTools.get("zoomOut");
1704
                ViewPort vp=getViewPort();
1705
                Envelope r=getViewPort().getAdjustedExtent();
1706
                Point2D pCenter=vp.fromMapPoint(r.getCenter(0),r.getCenter(1));
1707
                MouseEvent e=new MouseEvent(this,MouseEvent.MOUSE_RELEASED,MouseEvent.ACTION_EVENT_MASK,MouseEvent.BUTTON1,(int)pCenter.getX(),(int)pCenter.getY(),1,true,MouseEvent.BUTTON1);
1708
                try {
1709
                        mapTool.mousePressed(e);
1710
                        mapTool.mouseReleased(e);
1711
                } catch (BehaviorException t) {
1712
                        throwException(t);
1713
                }
1714
        }
1715

    
1716
        /**
1717
         * <p>Returns the listener used to catch all mouse events produced in this <code>MapControl</code> instance
1718
         *  and that redirects the calls to the current map tool.</p>
1719
         *
1720
         * @return the map tool listener used
1721
         */
1722
        public MapToolListener getMapToolListener() {
1723
                return mapToolListener;
1724
        }
1725

    
1726
        //         mapTool can be null, for instance, in 3D's navigation tools
1727
        /**
1728
         * <p>Sets <code>mapTool</code> as this <code>MapControl</code>'s current map tool.
1729
         *
1730
         * @param mapTool a map tool, or <code>null</code> to disable the interaction with the user
1731
         *
1732
         * @see #getCurrentMapTool()
1733
         * @see #getCurrentTool()
1734
         * @see #setTool(String)
1735
         * @see #setPrevTool()
1736
         * @see #addBehavior(String, Behavior)
1737
         * @see #addBehavior(String, Behavior[])
1738
         */
1739
        public void setCurrentMapTool(Behavior mapTool ){
1740
                currentMapTool = mapTool;
1741
        }
1742

    
1743
        /**
1744
         * <p>Sets the delay to the timer that refreshes this <code>MapControl</code> instance.</p>
1745
         *
1746
         * <p><code>Delay (in ms) = 1000 / getDrawFrameRate()</code></p>
1747
         *
1748
         * @see #getDrawFrameRate()
1749
         * @see #setDrawFrameRate(int)
1750
         */
1751
        public void applyFrameRate() {
1752
                if (MapContext.getDrawFrameRate()>0) {
1753
                        timer.setDelay(1000/MapContext.getDrawFrameRate());
1754
                }
1755
        }
1756

    
1757

    
1758

    
1759
        /**
1760
         * <p>Determines if its enabled the repaint that invokes the timer according to {@link #getDrawFrameRate() #getDrawFrameRate()}.</p>
1761
         *
1762
         * @return <code>true</code> if its enabled; otherwise <code>false</code>
1763
         */
1764
        public static boolean isDrawAnimationEnabled() {
1765
                return drawAnimationEnabled;
1766
        }
1767

    
1768
        /**
1769
         * <p>Sets if its enabled the repaint that invokes the timer according to {@link #getDrawFrameRate() #getDrawFrameRate()}.</p>
1770
         *
1771
         * @param drawAnimationEnabled <code>true</code> to enable the mode; otherwise <code>false</code>
1772
         */
1773
        public static void setDrawAnimationEnabled(boolean drawAnimationEnabled) {
1774
                MapControl.drawAnimationEnabled = drawAnimationEnabled;
1775
        }
1776

    
1777
        /**
1778
         * <p>Gets the shared object that determines if a drawing process must be cancelled or can continue.</p>
1779
         *
1780
         * @return the shared object that determines if a drawing process must be cancelled or can continue
1781
         */
1782
        public CancelDraw getCanceldraw() {
1783
                return canceldraw;
1784
        }
1785

    
1786
        /**
1787
         * <p>Gets the tool used in combination with the current tool of this <code>MapControl</code>.</p>
1788
         *
1789
         * @return the tool used in combination with the <code>currentMapTool</code>; <code>null</code> if there is
1790
         *  no combined tool
1791
         */
1792
        public Behavior getCombinedTool() {
1793
                return combinedTool;
1794
        }
1795

    
1796
        /**
1797
         * <p>Sets a tool to be used in combination with the current tool of this <code>MapControl</code>.</p>
1798
         *
1799
         * @param combinedTool a tool to be used in combination with the current tool of <code>MapControl</code>
1800
         */
1801
        public void setCombinedTool(Behavior combinedTool) {
1802
                this.combinedTool = combinedTool;
1803

    
1804
                if (currentMapTool == null) {
1805
                        return;
1806
                }
1807

    
1808
                if (currentMapTool instanceof CompoundBehavior) {
1809
                        ((CompoundBehavior)currentMapTool).addMapBehavior(combinedTool, true);
1810
                }
1811
                else {
1812
                        currentMapTool = new CompoundBehavior(new Behavior[] {currentMapTool});
1813
                        ((CompoundBehavior)currentMapTool).addMapBehavior(combinedTool, true);
1814
                }
1815

    
1816
        }
1817
        /**
1818
         * <p>Adds a new tool as combined tool.</p>
1819
         * <p>The new tool will be stored with the previous combined tools, and will be combined with
1820
         *  the current tool.</p>
1821
         * <p>If <code>tool</code> was already stored as a combined tool, doesn't adds it.</p>
1822
         *
1823
         * @param tool a new tool to be used combined with the current tool
1824
         */
1825
        public void addCombinedBehavior(Behavior tool) {
1826
                if (combinedTool == null) {
1827
                        combinedTool = tool;
1828
                }
1829
                else {
1830
                        if (combinedTool instanceof CompoundBehavior) {
1831
                                if (((CompoundBehavior)combinedTool).containsBehavior(tool)) {
1832
                                        return;
1833
                                }
1834

    
1835
                                ((CompoundBehavior)combinedTool).addMapBehavior(tool, true);
1836
                        }
1837
                        else {
1838
                                if (combinedTool.equals(tool)) {
1839
                                        return;
1840
                                }
1841

    
1842
                                combinedTool = new CompoundBehavior(new Behavior[] {combinedTool});
1843
                                ((CompoundBehavior)combinedTool).addMapBehavior(tool, true);
1844
                        }
1845
                }
1846

    
1847
                if (currentMapTool == null) {
1848
                        return;
1849
                }
1850

    
1851
                if (currentMapTool instanceof CompoundBehavior) {
1852
                        ((CompoundBehavior)currentMapTool).addMapBehavior(tool, true);
1853
                }
1854
                else {
1855
                        currentMapTool = new CompoundBehavior(new Behavior[] {currentMapTool});
1856
                        ((CompoundBehavior)currentMapTool).addMapBehavior(tool, true);
1857
                }
1858
        }
1859
        /**
1860
         * <p>Removes the tool <code>tool</code> used in combination with the current tool of this <code>MapControl</code>.</p>
1861
         */
1862
        public void removeCombinedTool(Behavior tool) {
1863
                if ((currentMapTool != null) && (currentMapTool instanceof CompoundBehavior)) {
1864
                        ((CompoundBehavior)currentMapTool).removeMapBehavior(tool);
1865
                }
1866

    
1867
                if (combinedTool == null) {
1868
                        return;
1869
                }
1870

    
1871
                if (combinedTool instanceof CompoundBehavior) {
1872
                        ((CompoundBehavior)combinedTool).removeMapBehavior(tool);
1873
                }
1874
                else {
1875
                        combinedTool = null;
1876
                }
1877
        }
1878

    
1879

    
1880
        /**
1881
         * <p>Updates the grid on the <code>ViewPort</code> of the associated <code>MapControl</code>
1882
         *  object according the values in the {@link com.iver.cit.gvsig.gui.cad.CADToolAdapter.prefs.Preferences com.iver.cit.gvsig.gui.cad.CADToolAdapter.prefs.Preferences}.</p>
1883
         *
1884
         * <p>The preferences are:
1885
         *  <ul>
1886
         *   <li>Show/hide the grid.</li>
1887
         *   <li>Adjust or not the grid.</li>
1888
         *   <li>Horizontal ( X ) line separation.</li>
1889
         *   <li>Vertical ( Y ) line separation.</li>
1890
         *  </ul>
1891
         * </p>
1892
         */
1893
        public void initializeGrid(){
1894
                Preferences prefs = mapControlManager.getEditionPreferences();
1895
                boolean showGrid = prefs.getBoolean("grid.showgrid",cadgrid.isShowGrid());
1896
                boolean adjustGrid = prefs.getBoolean("grid.adjustgrid",cadgrid.isAdjustGrid());
1897

    
1898
                double dx = prefs.getDouble("grid.distancex",cadgrid.getGridSizeX());
1899
                double dy = prefs.getDouble("grid.distancey",cadgrid.getGridSizeY());
1900

    
1901
                setGridVisibility(showGrid);
1902
                setAdjustGrid(adjustGrid);
1903
                cadgrid.setGridSizeX(dx);
1904
                cadgrid.setGridSizeY(dy);
1905
        }
1906

    
1907
        public void setAdjustGrid(boolean adjustGrid) {
1908
                cadgrid.setAdjustGrid(adjustGrid);
1909
        }
1910

    
1911
        public void setGridVisibility(boolean showGrid) {
1912
                cadgrid.setShowGrid(showGrid);
1913
                cadgrid.setViewPort(getViewPort());
1914
                this.repaint();
1915
        }
1916

    
1917
        /**
1918
         * Uses like a mouse pointer the image that provides the
1919
         * selected tool.
1920
         */
1921
        private void setToolMouse(){
1922
                Image cursor=getCurrentMapTool().getImageCursor();
1923
                Toolkit toolkit = Toolkit.getDefaultToolkit();
1924
                Cursor c = toolkit.createCustomCursor(cursor , 
1925
                                new Point(16, 16), "img");
1926
                setCursor (c);
1927
        }
1928

    
1929
        /**
1930
         * Makes the mouse pointer transparent.
1931
         */
1932
        private void setTransparentMouse(){
1933
                Image cursor=getCurrentMapTool().getImageCursor();
1934
                Toolkit toolkit = Toolkit.getDefaultToolkit();
1935
                Cursor transparentCursor = Toolkit.getDefaultToolkit()
1936
                .createCustomCursor(Toolkit.getDefaultToolkit().createImage(new MemoryImageSource(
1937
                                16, 16, new int[16 * 16], 0, 16)),
1938
                                new Point(0, 0), "invisiblecursor");
1939
                setCursor (transparentCursor);
1940
        }
1941

    
1942
        /**
1943
         * <p>Draws a 31x31 pixels cross round the mouse's cursor with an small geometry centered:
1944
         *  <ul>
1945
         *   <li><i>an square centered</i>: if isn't over a <i>control point</i>.
1946
         *   <li><i>an small geometry centered according to the kind of control point</i>: if it's over a control
1947
         *    point. In this case, the small geometry is drawn by a {@link ISnapper ISnapper} type object.<br>
1948
         *    On the other hand, a light-yellowed background tool tip text with the type of <i>control point</i> will
1949
         *     be displayed.</li>
1950
         * </p>
1951
         *
1952
         * @param g <code>MapControl</code>'s graphics where the data will be drawn
1953
         */
1954
        private void drawCursor() {
1955
                if (adjustedPoint == null){
1956
                        return;
1957
                }
1958

    
1959
                if (usedSnap != null){
1960
                        usedSnap.draw(mapControlDrawer, adjustedPoint);
1961
                        setTransparentMouse();                        
1962
                }else{
1963
                        setToolMouse();
1964
                }                
1965
        }
1966

    
1967
        /**
1968
         * <p>Adjusts the <code>point</code> to the grid if its enabled, and
1969
         *  sets <code>mapHandlerAdjustedPoint</code> with that new value.</p>
1970
         *
1971
         * <p>The value returned is the distance between those points: the original and
1972
         *  the adjusted one.</p>
1973
         *
1974
         * @param point point to adjust
1975
         * @param mapHandlerAdjustedPoint <code>point</code> adjusted
1976
         *
1977
         * @return distance from <code>point</code> to the adjusted one. If there is no
1978
         *  adjustment, returns <code>Double.MAX_VALUE</code>.
1979
         */
1980

    
1981

    
1982
        private double adjustToHandler(Point2D point,
1983
                        Point2D mapHandlerAdjustedPoint) {
1984

    
1985
                if (!isRefentEnabled())
1986
                        return Double.MAX_VALUE;
1987

    
1988
                ArrayList layersToSnap = getMapContext().getLayersToSnap();
1989

    
1990
                double mapTolerance = vp.toMapDistance(mapControlManager.getTolerance());
1991
                double minDist = mapTolerance;
1992
                double middleTol=mapTolerance * 0.5;
1993
                Point2D mapPoint = point;
1994
                org.gvsig.fmap.geom.primitive.Envelope r;
1995
                com.vividsolutions.jts.geom.Envelope e = null;
1996
                try {
1997
                        r = geomManager.createEnvelope(mapPoint.getX() - middleTol,
1998
                                        mapPoint.getY() - middleTol,
1999
                                        mapPoint.getX() + middleTol,
2000
                                        mapPoint.getY() + middleTol,
2001
                                        SUBTYPES.GEOM2D);
2002

    
2003
                        e = Converter.convertEnvelopeToJTS(r);
2004
                } catch (CreateEnvelopeException e1) {
2005
                        logger.error("Error creating the envelope", e1);
2006
                }
2007

    
2008
                usedSnap = null;
2009
                Point2D lastPoint = null;
2010
                if (previousPoint != null)
2011
                {
2012
                        lastPoint = new Point2D.Double(previousPoint[0], previousPoint[1]);
2013
                }
2014
                for (int j = 0; j < layersToSnap.size(); j++)
2015
                {
2016
                        FLyrVect lyrVect = (FLyrVect) layersToSnap.get(j);
2017
                        SpatialCache cache = lyrVect.getSpatialCache();
2018
                        if (lyrVect.isVisible())
2019
                        {
2020
                                // La lista de snappers est? siempre ordenada por prioridad. Los de mayor
2021
                                // prioridad est?n primero.
2022
                                java.util.List geoms = cache.query(e);
2023
                                for (int n=0; n < geoms.size(); n++) {
2024
                                        Geometry geom = (Geometry) geoms.get(n);
2025
                                        for (int i = 0; i < mapControlManager.getSnapperCount(); i++)
2026
                                        {
2027
                                                ISnapper theSnapper = mapControlManager.getSnapperAt(i);
2028

    
2029
                                                if (usedSnap != null)
2030
                                                {
2031
                                                        // Si ya tenemos un snap y es de alta prioridad, cogemos ese. (A no ser que en otra capa encontremos un snapper mejor)
2032
                                                        if (theSnapper.getPriority() > usedSnap.getPriority())
2033
                                                                break;
2034
                                                }
2035
                                                //                        SnappingVisitor snapVisitor = null;
2036
                                                Point2D theSnappedPoint = null;
2037
                                                if (theSnapper instanceof ISnapperVectorial)
2038
                                                {
2039
                                                        theSnappedPoint = ((ISnapperVectorial) theSnapper).getSnapPoint(point, geom, mapTolerance, lastPoint);
2040
                                                }
2041
                                                if (theSnapper instanceof ISnapperRaster)
2042
                                                {
2043
                                                        ISnapperRaster snapRaster = (ISnapperRaster) theSnapper;
2044
                                                        theSnappedPoint = snapRaster.getSnapPoint(this, point, mapTolerance, lastPoint);
2045
                                                }
2046

    
2047

    
2048
                                                if (theSnappedPoint != null) {
2049
                                                        double distAux = theSnappedPoint.distance(point);
2050
                                                        if (minDist > distAux)
2051
                                                        {
2052
                                                                minDist = distAux;
2053
                                                                usedSnap = theSnapper;
2054
                                                                mapHandlerAdjustedPoint.setLocation(theSnappedPoint);
2055
                                                        }
2056
                                                }
2057
                                        }
2058
                                } // for n
2059
                        } // visible
2060
                }
2061
                if (usedSnap != null)
2062
                        return minDist;
2063
                return Double.MAX_VALUE;
2064

    
2065
        }
2066
        /**
2067
         * Determines if snap tools are enabled or disabled.
2068
         *
2069
         * @return <code>true</code> to enable the snap tools; <code>false</code> to disable them
2070
         *
2071
         * @see #setRefentEnabled(boolean)
2072
         */
2073
        public boolean isRefentEnabled()
2074
        {
2075
                return bRefent;
2076
        }
2077

    
2078
        /**
2079
         * <p>Tries to find the nearest geometry or grid control point by the position of the current snap tool.</p>
2080
         *
2081
         * <p>Prioritizes the grid control points than the geometries ones.</p>
2082
         *
2083
         * <p>If finds any near, stores the <i>map</i> and <i>pixel</i> coordinates for the snap, and enables
2084
         *  the <code>bForceCoord</code> attribute for the next draw of the mouse's cursor.</p>
2085
         *
2086
         * @param point current mouse 2D position
2087
         */
2088
        public void calculateSnapPoint(Point point) {
2089
                // Se comprueba el ajuste a rejilla
2090

    
2091
                Point2D gridAdjustedPoint = vp.toMapPoint(
2092
                                point);
2093
                double minDistance = Double.MAX_VALUE;
2094

    
2095
                minDistance = cadgrid.adjustToGrid(gridAdjustedPoint);
2096
                if (minDistance < Double.MAX_VALUE) {
2097
                        adjustedPoint = vp.fromMapPoint(
2098
                                        gridAdjustedPoint);
2099
                        mapAdjustedPoint = gridAdjustedPoint;
2100
                } else {
2101
                        mapAdjustedPoint = null;
2102
                }
2103

    
2104
                Point2D handlerAdjustedPoint = null;
2105

    
2106
                // Se comprueba el ajuste a los handlers
2107
                if (mapAdjustedPoint != null) {
2108
                        handlerAdjustedPoint = (Point2D) mapAdjustedPoint.clone(); // getMapControl().getViewPort().toMapPoint(point);
2109
                } else {
2110
                        handlerAdjustedPoint = vp.toMapPoint(
2111
                                        point);
2112
                }
2113

    
2114
                Point2D mapPoint = new Point2D.Double();
2115
                double distance = adjustToHandler(handlerAdjustedPoint, mapPoint);
2116

    
2117
                if (distance < minDistance) {
2118
                        bForceCoord = true;
2119
                        adjustedPoint = vp.fromMapPoint(mapPoint);
2120
                        mapAdjustedPoint = mapPoint;
2121
                        minDistance = distance;
2122
                }
2123

    
2124
                // Si no hay ajuste
2125
                if (minDistance == Double.MAX_VALUE) {
2126
                        adjustedPoint = point;
2127
                        mapAdjustedPoint = null;
2128
                }                
2129
        }
2130

    
2131
        /**
2132
         * Sets the snap tools enabled or disabled.
2133
         *
2134
         * @param activated <code>true</code> to enable the snap tools; <code>false</code> to disable them
2135
         *
2136
         * @see #isRefentEnabled()
2137
         */
2138
        public void setRefentEnabled(boolean activated) {
2139
                bRefent = activated;
2140
        }
2141

    
2142
        public Grid getGrid() {
2143
                return cadgrid;
2144
        }
2145

    
2146
        public void update(Observable observable, Object notification) {
2147
                DataStoreNotification ddsn=(DataStoreNotification)notification;
2148
                String type=ddsn.getType();
2149
                if (type.equals(FeatureStoreNotification.AFTER_UNDO) ||
2150
                                type.equals(FeatureStoreNotification.AFTER_REDO)){
2151
                        repaint();
2152
                }
2153
        }        
2154

    
2155
        /**
2156
         * @return the adjustedPoint
2157
         */
2158
        public Point2D getAdjustedPoint() {
2159
                return adjustedPoint;
2160
        }
2161

    
2162
        /**
2163
         * @return the mapAdjustedPoint
2164
         */
2165
        public Point2D getMapAdjustedPoint() {
2166
                return mapAdjustedPoint;
2167
        }
2168

    
2169
        /**
2170
         * Gets the selected point. If the snapping is enabled
2171
         * it returns the selected point.
2172
         * @return
2173
         * The selected point
2174
         */
2175
        public Point2D getPoint(){
2176
                if (mapAdjustedPoint != null) {
2177
                        return mapAdjustedPoint;
2178
                } else {
2179
                        return getViewPort().toMapPoint(adjustedPoint);
2180
                }
2181
        }        
2182
}