Statistics
| Revision:

svn-gvsig-desktop / trunk / org.gvsig.desktop / org.gvsig.desktop.library / org.gvsig.fmap.control / src / main / java / org / gvsig / fmap / mapcontrol / MapControl.java @ 45610

History | View | Annotate | Download (90.1 KB)

1
/**
2
 * gvSIG. Desktop Geographic Information System.
3
 *
4
 * Copyright (C) 2007-2013 gvSIG Association.
5
 *
6
 * This program is free software; you can redistribute it and/or
7
 * modify it under the terms of the GNU General Public License
8
 * as published by the Free Software Foundation; either version 3
9
 * of the License, or (at your option) any later version.
10
 *
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License
17
 * along with this program; if not, write to the Free Software
18
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19
 * MA  02110-1301, USA.
20
 *
21
 * For any additional information, do not hesitate to contact us
22
 * at info AT gvsig.com, or visit our website www.gvsig.com.
23
 */
24
package org.gvsig.fmap.mapcontrol;
25

    
26
import java.awt.Color;
27
import java.awt.Cursor;
28
import java.awt.Dimension;
29
import java.awt.Graphics;
30
import java.awt.Graphics2D;
31
import java.awt.Image;
32
import java.awt.Point;
33
import java.awt.Toolkit;
34
import java.awt.event.ActionEvent;
35
import java.awt.event.ActionListener;
36
import java.awt.event.ComponentEvent;
37
import java.awt.event.ComponentListener;
38
import java.awt.event.MouseEvent;
39
import java.awt.event.MouseListener;
40
import java.awt.event.MouseMotionListener;
41
import java.awt.event.MouseWheelEvent;
42
import java.awt.event.MouseWheelListener;
43
import java.awt.geom.Point2D;
44
import java.awt.image.BufferedImage;
45
import java.awt.image.MemoryImageSource;
46
import java.util.Comparator;
47
import java.util.HashMap;
48
import java.util.List;
49
import java.util.Map;
50
import java.util.Set;
51
import java.util.TreeMap;
52
import java.util.prefs.Preferences;
53

    
54
import javax.swing.JComponent;
55
import javax.swing.SwingUtilities;
56
import javax.swing.Timer;
57
import javax.swing.event.ChangeEvent;
58
import javax.swing.event.ChangeListener;
59

    
60
import org.cresques.cts.IProjection;
61
import org.gvsig.fmap.dal.DataStoreNotification;
62
import org.gvsig.fmap.dal.feature.FeatureStoreNotification;
63
import org.gvsig.fmap.geom.Geometry;
64
import org.gvsig.fmap.geom.Geometry.SUBTYPES;
65
import org.gvsig.fmap.geom.GeometryLocator;
66
import org.gvsig.fmap.geom.GeometryManager;
67
import org.gvsig.fmap.geom.exception.CreateEnvelopeException;
68
import org.gvsig.fmap.geom.primitive.Envelope;
69
import org.gvsig.fmap.mapcontext.MapContext;
70
import org.gvsig.fmap.mapcontext.MapContextLocator;
71
import org.gvsig.fmap.mapcontext.ViewPort;
72
import org.gvsig.fmap.mapcontext.events.AtomicEvent;
73
import org.gvsig.fmap.mapcontext.events.listeners.AtomicEventListener;
74
import org.gvsig.fmap.mapcontext.layers.FLayer;
75
import org.gvsig.fmap.mapcontext.layers.FLayers;
76
import org.gvsig.fmap.mapcontext.layers.LayerCollectionEvent;
77
import org.gvsig.fmap.mapcontext.layers.LayerEvent;
78
import org.gvsig.fmap.mapcontext.layers.SpatialCache;
79
import org.gvsig.fmap.mapcontext.layers.vectorial.FLyrVect;
80
import org.gvsig.fmap.mapcontext.layers.vectorial.GraphicLayer;
81
import org.gvsig.fmap.mapcontrol.tools.BehaviorException;
82
import org.gvsig.fmap.mapcontrol.tools.CompoundBehavior;
83
import org.gvsig.fmap.mapcontrol.tools.Behavior.Behavior;
84
import org.gvsig.fmap.mapcontrol.tools.Listeners.ToolListener;
85
import org.gvsig.fmap.mapcontrol.tools.grid.Grid;
86
import org.gvsig.fmap.mapcontrol.tools.snapping.snappers.ISnapper;
87
import org.gvsig.fmap.mapcontrol.tools.snapping.snappers.ISnapperGeometriesVectorial;
88
import org.gvsig.fmap.mapcontrol.tools.snapping.snappers.ISnapperRaster;
89
import org.gvsig.fmap.mapcontrol.tools.snapping.snappers.ISnapperVectorial;
90
import org.gvsig.tools.ToolsLocator;
91
import org.gvsig.tools.dispose.Disposable;
92
import org.gvsig.tools.observer.Observable;
93
import org.gvsig.tools.observer.Observer;
94
import org.gvsig.tools.swing.api.ActionListenerSupport;
95
import org.gvsig.tools.swing.api.ChangeListenerHelper;
96
import org.gvsig.tools.swing.api.ToolsSwingLocator;
97
import org.gvsig.tools.task.Cancellable;
98
import org.gvsig.utils.exceptionHandling.ExceptionHandlingSupport;
99
import org.gvsig.utils.exceptionHandling.ExceptionListener;
100
import org.slf4j.Logger;
101
import org.slf4j.LoggerFactory;
102

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

    
266
    public class AddLayerEvent extends ActionEvent {
267

    
268
        private static final long serialVersionUID = -1857839388303164091L;
269

    
270
        private FLayer layer;
271
        
272
        public AddLayerEvent(FLayer layer, Object source, int id, String command) {
273
            super(source, id, command);
274
            this.layer = layer;
275
        }
276
        
277
        public FLayer getLayer() {
278
            return this.layer;
279
        }
280
        
281
        public void setLayer(FLayer layer) {
282
            this.layer = layer;
283
        }
284
    }
285
    
286
    protected static final GeometryManager geomManager = GeometryLocator.getGeometryManager();
287
    private static final Logger LOG = LoggerFactory.getLogger(GeometryManager.class);
288

    
289
    /**
290
     * <p>
291
     * One of the possible status of <code>MapControl</code>. Determines that
292
     * all visible information has been drawn and its updated.
293
     * </p>
294
     */
295
    public static final int ACTUALIZADO = 0;
296

    
297
    /**
298
     * <p>
299
     * One of the possible status of <code>MapControl</code>. Determines that
300
     * not all visible information has been drawn or isn't updated.
301
     * </p>
302
     */
303
    public static final int DESACTUALIZADO = 1;
304

    
305
    /**
306
     * <p>
307
     * Determines if the drawer can update this <code>MapControl</code> instance
308
     * when the timer launches an event.
309
     * </p>
310
     */
311
    private static boolean drawAnimationEnabled = true;
312

    
313
    public static final int PAINT_PARTIAL_DRAWING_LAYERS = 0;
314
    public static final int PAINT_WHEN_DRAW_LAYERS_IS_COMPLETED = 1;
315

    
316
    private int paintMode = PAINT_PARTIAL_DRAWING_LAYERS;
317
    
318
    /**
319
     * <p>
320
     * Inner model with the layers, event support for drawing them, and the
321
     * <code>ViewPort</code> with information to adapt to the bounds available
322
     * in <i>image coordinates</i>.
323
     * </p>
324
     *
325
     * @see #getMapContext()
326
     * @see #setMapContext(MapContext)
327
     */
328
    private MapContext mapContext = null;
329

    
330
    /**
331
     * <p>
332
     * All registered <code>Behavior</code> that can define a way to work with
333
     * this <code>MapControl</code>.
334
     * </p>
335
     *
336
     * <p>
337
     * Only one of them can be active at a given moment.
338
     * </p>
339
     *
340
     * @see #addBehavior(String, Behavior)
341
     * @see #addBehavior(String, Behavior[])
342
     * @see #getMapTool(String)
343
     * @see #getMapToolsKeySet()
344
     * @see #getNamesMapTools()
345
     */
346
    protected Map<String,Behavior> namesMapTools = new HashMap<String,Behavior>();
347

    
348
    /**
349
     * <p>
350
     * Active {@link Behavior Behavior} that will generate events according a
351
     * criterion, and then, with a {@link ToolListener ToolListener} associated,
352
     * will simulate to user that works with this component as a particular
353
     * tool.
354
     * </p>
355
     *
356
     * @see #getCurrentMapTool()
357
     * @see #getCurrentTool()
358
     * @see #setTool(String)
359
     */
360
    protected Behavior currentMapTool = null;
361

    
362
    /**
363
     * <p>
364
     * Determines which's the current drawn status of this component:
365
     * <ul>
366
     * <li><b>OUTDATED</b>: all visible information has been drawn or isn't
367
     * updated.</li>
368
     * <li><b>UTDATED</b>: all visible information has been drawn and its
369
     * updated.</li>
370
     * <li><b>ONLY_GRAPHICS</b>: only the graphical layer must be drawn /
371
     * updated.</li>
372
     * </ul>
373
     * </p>
374
     *
375
     * <p>
376
     * The <code>MapControl</code> drawing process will consider the value of
377
     * this parameter to decide which elements will be updated or drawn.
378
     * </p>
379
     */
380
    private int status = DESACTUALIZADO;
381

    
382
    /**
383
     * <p>
384
     * Image with a buffer to accelerate the draw the changes of the graphical
385
     * items in this component.
386
     * </p>
387
     *
388
     * <p>
389
     * Firstly, information will be drawn in the buffer, and, when is outright
390
     * drawn, that information will be displayed. Meanwhile, the previous image
391
     * can be kept showed.
392
     * </p>
393
     *
394
     * @see BufferedImage
395
     *
396
     * @see #getImage()
397
     */
398
    private BufferedImage image = null;
399

    
400
    /**
401
     * <p>
402
     * Name of the tool used currently to interact with this component.
403
     * </p>
404
     *
405
     * @see #getCurrentTool()
406
     * @see #setTool(String)
407
     */
408
    protected String currentTool;
409

    
410
    /**
411
     * <p>
412
     * Object to store the flag that notifies a drawing thread task and
413
     * <code>MapContext</code>'s layers, that must be canceled or can continue
414
     * with the process.
415
     * </p>
416
     *
417
     * @see #cancelDrawing()
418
     */
419
    private CancelDraw canceldraw;
420

    
421
    // private boolean isCancelled = true;
422

    
423
    /**
424
     * <p>
425
     * Fires an action events after a specified delay.
426
     * </p>
427
     *
428
     * <p>
429
     * <code>MapControl</code> will use the timer to update its visible
430
     * graphical information during a drawing process, or allowing to cancel
431
     * that process.
432
     * </p>
433
     *
434
     * <p>
435
     * This is very useful to pretend faster interactivity to user when
436
     * <code>MapControl</code> has lots of layers, and / or layers with heavy
437
     * graphical elements, that need a long time to finish drawing all its data.
438
     * </p>
439
     */
440
    private Timer timer;
441

    
442
    /**
443
     * <p>
444
     * Reference to the {@link ViewPort ViewPort} of the {@link MapContext
445
     * MapContext} of this component.
446
     * </p>
447
     *
448
     * <p>
449
     * After, the view port will change adapting itself according the current
450
     * projection and the extent.
451
     * </p>
452
     *
453
     * @see #getViewPort()
454
     *
455
     * @see ViewPort
456
     */
457
    protected ViewPort vp;
458

    
459
    /**
460
     * <p>
461
     * Manager of all <code>MapControl</code> painting requests.
462
     * </p>
463
     */
464
    private Drawer drawer;
465

    
466
    /**
467
     * <p>
468
     * Listener of all kind of mouse events produced in this component.
469
     * </p>
470
     *
471
     * <p>
472
     * Delegates each mouse event to the current map tool.
473
     * </p>
474
     *
475
     * @see #addBehavior(String, Behavior)
476
     * @see #addBehavior(String, Behavior[])
477
     * @see #getMapTool(String)
478
     * @see #getMapToolsKeySet()
479
     * @see #getNamesMapTools()
480
     * @see #setTool(String)
481
     */
482
    protected MapToolListener mapToolListener = new MapToolListener();
483

    
484
    /**
485
     * <p>
486
     * Listener of all events produced in a this component's
487
     * <code>MapContext</code> object during an atomic period of time.
488
     * </p>
489
     */
490
    private MapContextListener mapContextListener = new MapContextListener();
491

    
492
    /**
493
     * <p>
494
     * Group of <code>ExceptionListener</code> that, in whatever moment could be
495
     * notified a Throwable Java error or exception.
496
     * </p>
497
     *
498
     * @see #addExceptionListener(ExceptionListener)
499
     * @see #removeExceptionListener(ExceptionListener)
500
     */
501
    private ExceptionHandlingSupport exceptionHandlingSupport =
502
        new ExceptionHandlingSupport();
503

    
504
    /**
505
     * <p>
506
     * Name of the previous tool used.
507
     * </p>
508
     */
509
    protected String prevTool;
510

    
511
    /**
512
     * <p>
513
     * Tool that will be used combined with the current tool of this
514
     * <code>MapControl</code>.
515
     * </p>
516
     */
517
    private Behavior combinedTool = null;
518

    
519
    /**
520
     * Optional grid that could be applied on the <code>MapControl</code>'s view
521
     * port.
522
     *
523
     * @see #getGrid()
524
     * @see #setAdjustGrid(boolean)
525
     */
526
    private Grid cadgrid = new Grid();
527
    /**
528
     * Represents the cursor's point selected in <i>screen coordinates</i>.
529
     *
530
     * @see ViewPort#fromMapPoint(Point2D)
531
     */
532
    protected Point2D adjustedPoint;
533
    /**
534
     * <p>
535
     * Determines if the position of the snap of the mouse's cursor on the
536
     * <code>MapControl</code> is within the area around a control point of a
537
     * geometry.
538
     * </p>
539
     *
540
     * <p>
541
     * The area is calculated as a circle centered at the control point and with
542
     * radius the pixels tolerance defined in the preferences.
543
     * </p>
544
     */
545
    private boolean bForceCoord = false;
546

    
547
    /**
548
     * Kind of geometry drawn to identify the kind of control point selected by
549
     * the cursor's mouse.
550
     */
551
    private ISnapper usedSnap = null;
552

    
553
    /**
554
     * Determines if the snap tools are enabled or disabled.
555
     *
556
     * @see #isRefentEnabled()
557
     * @see #setRefentEnabled(boolean)
558
     */
559
    private boolean bRefent = true;
560

    
561
    /**
562
     * Stores the 2D map coordinates of the last point added.
563
     */
564
    private double[] previousPoint = null;
565

    
566
    private boolean defaultMouseWheelEnabled = true;
567
    
568
    protected static MapControlManager mapControlManager =
569
        MapControlLocator.getMapControlManager();
570

    
571
    private static TreeMap selected = new TreeMap(new Comparator() {
572

    
573
        public int compare(Object o1, Object o2) {
574
            if (o1.getClass().equals(o2.getClass()))
575
                return 0;
576
            if (((ISnapper) o1).getPriority() > ((ISnapper) o2).getPriority())
577
                return 1;
578
            else
579
                return -1;
580
        }
581

    
582
    });
583

    
584
    /**
585
     * Represents the cursor's point selected in <i>map coordinates</i>.
586
     *
587
     * @see MapControl#toMapPoint
588
     */
589
    protected Point2D mapAdjustedPoint;
590

    
591
    /**
592
     * Renderer used to draw the layers.
593
     */
594
    private MapControlDrawer mapControlDrawer = null;
595
        private Cursor transparentCursor;
596

    
597
        private boolean disposed = false;
598

    
599
    private ActionListenerSupport addLayerHelper;
600
    
601
    /**
602
     * <p>
603
     * Creates a new <code>MapControl</code> instance with the following
604
     * characteristics:
605
     * <ul>
606
     * <li><i>Name</i>: MapControl .</li>
607
     * <li>Disables the double buffer of <code>JComponent</code> .</li>
608
     * <li>Sets opaque <i>(see {@link JComponent#setOpaque(boolean)} )</i>.</li>
609
     * <li>Sets its status to <code>OUTDATED</code> .</li>
610
     * <li>Creates a new {@link CancelDraw CancelDraw} object to notify
611
     * <code>MapContext</code>'s layers if can continue processing the drawn or
612
     * must cancel it.</li>
613
     * <li>Creates a new {@link MapContext MapContext} with a new
614
     * {@link ViewPort ViewPort} in the default projection.</li>
615
     * <li>Creates a new {@link CommandListener CommandListener} for edition
616
     * operations.</li>
617
     * <li>Creates a new {@link MapToolListener MapToolListener}, and associates
618
     * it as a listener of whatever kind of mouse events produced in this
619
     * component.</li>
620
     * <li>Creates a new {@link Drawer2 Drawer2} for managing the painting
621
     * requests.</li>
622
     * <li>Creates a new timer that will invoke refresh this component
623
     * <code>drawFrameRate</code> per second, when is running a drawing process,
624
     * and its enabled <code>drawAnimationEnabled</code>.</li>
625
     * </ul>
626
     * </p>
627
     */
628
    public MapControl() {
629
        this(null);
630
    }
631

    
632
    public MapControl(MapContext theMapContext) {
633
        this.changeToolListenerHelper = ToolsSwingLocator.getToolsSwingManager().createChangeListenerHelper();
634
        if( theMapContext == null ) {
635
            theMapContext = new MapContext(new ViewPort(MapContextLocator.getMapContextManager().getDefaultCRS()));
636
        }
637
        this.setName("MapControl");
638
        this.addLayerHelper = ToolsSwingLocator.getToolsSwingManager().createActionListenerSupport();
639
        Toolkit toolkit = Toolkit.getDefaultToolkit();
640
        Image imageTransparentCursor = toolkit.createImage(new MemoryImageSource(16, 16, new int[16 * 16], 0,16));
641
        transparentCursor =
642
            toolkit.createCustomCursor(imageTransparentCursor, new Point(0, 0), "invisiblecursor");
643

    
644
        setDoubleBuffered(false);
645
        setOpaque(true);
646
        status = DESACTUALIZADO;
647

    
648
        // Clase usada para cancelar el dibujado
649
        canceldraw = new CancelDraw();
650

    
651
        vp = theMapContext.getViewPort();
652

    
653
        setMapContext(theMapContext);
654

    
655
        // eventos
656
        this.addComponentListener(this);
657
        this.addMouseListener(mapToolListener);
658
        this.addMouseMotionListener(mapToolListener);
659
        this.addMouseWheelListener(mapToolListener);
660

    
661
        this.drawer = new Drawer();
662
        // Timer para mostrar el redibujado mientras se dibuja
663
        timer = new Timer(1000 / MapContext.getDrawFrameRate(),
664
                new ActionListener() {
665
                    @Override
666
                    public void actionPerformed(ActionEvent e) {
667
                        if ( paintMode!=PAINT_WHEN_DRAW_LAYERS_IS_COMPLETED && drawAnimationEnabled) {
668
                            MapControl.this.repaint();
669
                        }
670
                    }
671
                }
672
        );
673
        initializeGrid();
674

    
675
        if(ToolsLocator.getDisposableManager() != null) {
676
                        ToolsLocator.getDisposableManager().bind(this);
677
                } else {
678
                        LOG.warn("Can't retrieve the disposable manager,");
679
                }
680
    }
681
    
682
    public void addLayer(FLayer layer) {
683
        AddLayerEvent event = new AddLayerEvent(layer, this, 1, "addLayer");
684
        this.addLayerHelper.fireActionEvent(event);
685
        this.mapContext.getLayers().add(event.getLayer());
686
        if( !this.mapContext.hasActiveLayers() ) {
687
          event.getLayer().setActive(true);
688
        }
689
    }
690
    
691
    public void addLayerListener(ActionListener al) {
692
        this.addLayerHelper.addActionListener(al);
693
    }
694
    
695
    public void setPaintMode(int paintMode) {
696
        this.paintMode = paintMode;
697
    }
698
    
699
    public int getPaintMode() {
700
        return this.paintMode;
701
    }
702
    
703
    /**
704
     * <p>
705
     * Sets a <code>MapContext</code> to this component.
706
     * </p>
707
     *
708
     * <p>
709
     * The <code>MapContext</code> has the <i>model</i>, and most of the
710
     * <i>view</i>, and <i>control</i> logic of the layers of this component,
711
     * including a {@link ViewPort ViewPort} to adapt the information to the
712
     * projection, and to display it in the available area.
713
     * </p>
714
     *
715
     * <p>
716
     * If <code>model</code> hadn't a <code>ViewPort</code>, assigns the current
717
     * one to it, otherwise, use its <code>ViewPort</code>.
718
     * </p>
719
     *
720
     * <p>
721
     * After assigning the <code>MapContext</code> and <code>ViewPort</code>,
722
     * sets the same {@link MapContextListener MapContextListener} that was
723
     * using, and changes the <i>status</i> to <code>OUTDATED</code>.
724
     * </p>
725
     *
726
     * @param model
727
     *            this component's <code>MapContext</code>, that includes the
728
     *            <code>ViewPort</code>.
729
     *
730
     * @see MapContext
731
     *
732
     * @see #getMapContext()
733
     */
734
    public void setMapContext(MapContext model) {
735
        if (mapContext != null) {
736
            mapContext.removeAtomicEventListener(mapContextListener);
737
            mapContext.dispose();
738
        }
739

    
740
        mapContext = model;
741

    
742
        if (mapContext.getViewPort() == null) {
743
            mapContext.setViewPort(vp);
744
        } else {
745
            vp = mapContext.getViewPort();
746
            cadgrid.setViewPort(vp);
747
        }
748

    
749
        mapContext.addAtomicEventListener(mapContextListener);
750

    
751
        status = DESACTUALIZADO;
752
    }
753

    
754
    /**
755
     * @return the mapControlDrawer
756
     */
757
    public MapControlDrawer getMapControlDrawer() {
758
        return mapControlDrawer;
759
    }
760

    
761
    /**
762
     * @param mapControlDrawer
763
     *            the mapControlDrawer to set
764
     */
765
    public void setMapControlDrawer(MapControlDrawer mapControlDrawer) {
766
        this.mapControlDrawer = mapControlDrawer;
767
        this.mapControlDrawer.setViewPort(vp);
768
    }
769

    
770
    /**
771
     * <p>
772
     * Gets this component's {@link MapContext MapContext} projection.
773
     * </p>
774
     *
775
     * @return this component's {@link MapContext MapContext} projection
776
     *
777
     * @see MapContext#getProjection()
778
     * @see MapControl#setProjection(IProjection)
779
     */
780
    public IProjection getProjection() {
781
        return getMapContext().getProjection();
782
    }
783

    
784
    /**
785
     * <p>
786
     * Sets the projection to this component's {@link MapContext MapContext}.
787
     * </p>
788
     *
789
     * @param proj
790
     *            the kind of projection to this component's {@link MapContext
791
     *            MapContext}
792
     *
793
     * @see MapContext#setProjection(IProjection)
794
     * @see MapControl#getProjection()
795
     */
796
    public void setProjection(IProjection proj) {
797
        getMapContext().setProjection(proj);
798
    }
799

    
800
    /**
801
     * <p>
802
     * Gets this component's <code>MapContext</code>, with the <i>model</i>, and
803
     * most of the <i>view</i>, and <i>control</i> logic of the layers of this
804
     * component, including a {@link ViewPort ViewPort} to adapt the information
805
     * to the projection, and display it in the available area.
806
     * </p>
807
     *
808
     * @return this component's <code>MapContext</code>, that includes the
809
     *         <code>ViewPort</code> used to project the
810
     *         graphical information, and display it in the available area
811
     *
812
     * @see MapContext
813
     *
814
     * @see MapControl#setMapContext(MapContext)
815
     */
816
    public MapContext getMapContext() {
817
        return mapContext;
818
    }
819

    
820
    /**
821
     * <p>
822
     * Registers a new behavior to this component.
823
     * </p>
824
     *
825
     * <p>
826
     * According the nature of the {@link Behavior Behavior}, different events
827
     * will be generated. Those events can be caught by a particular
828
     * {@link ToolListener ToolListener}, allowing user to interact with this
829
     * <code>MapControl</code> object as a <i>tool</i>.
830
     * </p>
831
     *
832
     * @param name
833
     *            name to identify the behavior to add
834
     * @param tool
835
     *            the behavior to add
836
     *
837
     * @see #addBehavior(String, Behavior[])
838
     * @see #getNamesMapTools()
839
     * @see #getMapToolsKeySet()
840
     * @see #hasTool(String)
841
     */
842
    public void addBehavior(String name, Behavior tool) {
843
        namesMapTools.put(name, tool);
844
        tool.setMapControl(this);
845
    }
846

    
847
    /**
848
     * <p>
849
     * Registers a new behavior to this component as a {@link CompoundBehavior
850
     * CompoundBehavior} made up of <code>tools</code>.
851
     * </p>
852
     *
853
     * <p>
854
     * According the nature of the behaviors registered, different events will
855
     * be generated. Those events can be caught by a particular
856
     * {@link ToolListener ToolListener}, allowing user to interact with this
857
     * <code>MapControl</code> object as a <i>tool</i>.
858
     * </p>
859
     *
860
     * @param name
861
     *            name to identify the compound behavior to add
862
     * @param tools
863
     *            the compound behavior to add
864
     *
865
     * @see #addBehavior(String, Behavior)
866
     * @see #getNamesMapTools()
867
     * @see #getMapToolsKeySet()
868
     * @see #hasTool(String)
869
     */
870
    public void addBehavior(String name, Behavior[] tools) {
871
        CompoundBehavior tool = new CompoundBehavior(tools);
872
        tool.setMapControl(this);
873
        addBehavior(name, tool);
874
    }
875

    
876
    /**
877
     * <p>
878
     * Gets the <code>Behavior</code> registered in this component, identified
879
     * by <code>name</code>.
880
     * </p>
881
     *
882
     * @param name
883
     *            name of a registered behavior
884
     *
885
     * @return tool the registered behavior in this component as
886
     *         <code>name</code>, or <code>null</code> if
887
     *         no one has that identifier
888
     *
889
     * @see #addBehavior(String, Behavior)
890
     * @see #addBehavior(String, Behavior[])
891
     * @see #hasTool(String)
892
     */
893
    public Behavior getMapTool(String name) {
894
        return (Behavior) namesMapTools.get(name);
895
    }
896

    
897
    /**
898
     * <p>
899
     * Returns a set view of the keys that identified the tools registered.
900
     * </p>
901
     *
902
     * @return a set view of the keys that identified the tools registered
903
     *
904
     * @see HashMap#keySet()
905
     *
906
     * @see #getNamesMapTools()
907
     * @see #addBehavior(String, Behavior)
908
     * @see #addBehavior(String, Behavior[])
909
     */
910
    public Set getMapToolsKeySet() {
911
        return namesMapTools.keySet();
912
    }
913

    
914
    /**
915
     * <p>
916
     * Returns <code>true</code> if this component contains a tool identified by
917
     * <code>toolName</code>.
918
     * </p>
919
     *
920
     * @param toolName
921
     *            identifier of the tool
922
     *
923
     * @return <code>true</code> if this component contains a tool identified by
924
     *         <code>toolName</code>; otherwise <code>false</code>
925
     *
926
     * @see #addBehavior(String, Behavior)
927
     * @see #addBehavior(String, Behavior[])
928
     */
929
    public boolean hasTool(String toolName) {
930
        return namesMapTools.containsKey(toolName);
931
    }
932

    
933
    /**
934
     * <p>
935
     * Sets as current active <code>Behavior</code> associated to this
936
     * component, that one which is registered and identified by
937
     * <code>toolName</code>.
938
     * </p>
939
     *
940
     * <p>
941
     * Changing the current active behavior for this <code>MapControl</code>,
942
     * implies also updating the previous <i>behavior</i> tool, and the current
943
     * cursor.
944
     * </p>
945
     *
946
     * @param toolName
947
     *            name of a registered behavior
948
     *
949
     * @see #getCurrentMapTool()
950
     * @see #getCurrentTool()
951
     */
952
    public void setTool(String toolName) {
953
        prevTool = getCurrentTool();
954
        Behavior mapTool = (Behavior) namesMapTools.get(toolName);
955
        currentMapTool = mapTool;
956
        currentTool = toolName;
957

    
958
        if (combinedTool != null) {
959
            if (mapTool instanceof CompoundBehavior) {
960
                ((CompoundBehavior) mapTool).addMapBehavior(combinedTool, true);
961
            } else {
962
                currentMapTool =
963
                    new CompoundBehavior(new Behavior[] { currentMapTool });
964
                ((CompoundBehavior) currentMapTool).addMapBehavior(
965
                    combinedTool, true);
966
                currentMapTool.setMapControl(this);
967
            }
968
        }
969
        this.changeToolListenerHelper.fireEvent(new ChangeEvent(this));
970
        // this.setCursor(mapTool.getCursor());
971
    }
972

    
973
    /**
974
     * <p>
975
     * Gets as current active <code>Behavior</code> associated to this
976
     * component, that one which is registered and identified by
977
     * <code>toolName</code>.
978
     * </p>
979
     *
980
     * <p>
981
     * Changing the current active behavior for this <code>MapControl</code>,
982
     * implies also updating the previous <i>behavior</i> tool, and the current
983
     * cursor.
984
     * </p>
985
     *
986
     * @param toolName
987
     *            name of a registered behavior
988
     *
989
     * @see #getCurrentTool()
990
     * @see #setTool(String)
991
     */
992
    public Behavior getCurrentMapTool() {
993
        return currentMapTool;
994
    }
995

    
996
    /**
997
     * <p>
998
     * Returns the name of the current selected tool on this MapControl
999
     * </p>
1000
     *
1001
     * @return the name of the current's behavior tool associated to this
1002
     *         component
1003
     *
1004
     * @see #getCurrentMapTool()
1005
     * @see #setTool(String)
1006
     */
1007
    public String getCurrentTool() {
1008
        return currentTool;
1009
    }
1010

    
1011
    /**
1012
     * <p>
1013
     * Determines that current drawing process of <code>MapControl</code>'s
1014
     * <code>MapContext</code>'s data must be canceled.
1015
     * </p>
1016
     *
1017
     * <p>
1018
     * It has no effects if now isn't drawing that graphical information.
1019
     * </p>
1020
     *
1021
     * <p>
1022
     * At last resort, the particular implementation of each layer in this
1023
     * <code>MapControl</code>'s <code>MapContrext</code> will be that one which
1024
     * will draw the graphical information, and, if supports, which could cancel
1025
     * its drawing subprocess.
1026
     * </p>
1027
     */
1028
    public void cancelDrawing() {
1029
        /*
1030
         * if (drawer != null) {
1031
         * if (!drawer.isAlive()) {
1032
         * return;
1033
         * }
1034
         * }
1035
         */
1036
        canceldraw.setCanceled(true);
1037

    
1038
        /*
1039
         * while (!isCancelled) {
1040
         * if (!drawer.isAlive()) {
1041
         * // Si hemos llegado aqu� con un thread vivo, seguramente
1042
         * // no estamos actualizados.
1043
         *
1044
         * break;
1045
         * }
1046
         *
1047
         * }
1048
         * canceldraw.setCancel(false);
1049
         * isCancelled = false;
1050
         * drawerAlive = false;
1051
         */
1052
    }
1053

    
1054
    /**
1055
     * <p>
1056
     * Creates a {@link BufferedImage BufferedImage} image if there was no
1057
     * buffered image, or if its viewport's image height or width is different
1058
     * from this component's size. Once has created a double-buffer, fills it
1059
     * with the vieport's background color, or with <i>white</i> if it had no
1060
     * background color.
1061
     * </p>
1062
     *
1063
     * <p>
1064
     * If no double-buffered existed, creates a {@link BufferedImage
1065
     * BufferedImage} with the size of this component, and as an image with
1066
     * 8-bit RGBA color components packed into integer pixels. That image has a
1067
     * <code>DirectColorModel</code> with alpha. The color data in that image is
1068
     * considered not to be premultiplied with alpha.
1069
     * </p>
1070
     *
1071
     * <p>
1072
     * Once has created and filled the new inner <code>MapControl</code>'s
1073
     * double-buffer, changes the status to <code>OUTDATED</code>.
1074
     * </p>
1075
     *
1076
     * @return <code>true</code> if has created and filled a new double-buffer
1077
     *         for this <code>MapControl</code> instance; otherwise
1078
     *         <code>false</code>
1079
     */
1080
    private boolean adaptToImageSize() {
1081
        if ((image == null) || (vp.getImageWidth() != this.getWidth())
1082
            || (vp.getImageHeight() != this.getHeight())) {
1083
            image = new BufferedImage(this.getWidth(), this.getHeight(),
1084
                    BufferedImage.TYPE_INT_ARGB);
1085
            vp.setImageSize(new Dimension(getWidth(), getHeight()));
1086
            getMapContext().getViewPort().refreshExtent();
1087
            clearImage(image);
1088
            status = DESACTUALIZADO;
1089
            return true;
1090
        }
1091
        return false;
1092
    }
1093

    
1094
    private void clearImage(BufferedImage image) {
1095
        if( image == null ) {
1096
            return;
1097
        }
1098
        Graphics gTemp = image.createGraphics();
1099
        Color theBackColor = vp.getBackColor();
1100
        if (theBackColor == null) {
1101
            gTemp.setColor(Color.WHITE);
1102
        } else {
1103
            gTemp.setColor(theBackColor);
1104
        }
1105
        gTemp.fillRect(0, 0, getWidth(), getHeight());
1106
        gTemp.dispose();
1107
    }
1108
    
1109
    /**
1110
     * <p>
1111
     * Paints the graphical information of this component using a double buffer.
1112
     * </p>
1113
     *
1114
     * <p>
1115
     * If the double buffer wasn't created, creates a new one.
1116
     * </p>
1117
     *
1118
     * <p>
1119
     * Paints the component according the following algorithm: <br>
1120
     * &nbsp If <i>status</i> is <i>UPDATED</i>:<br>
1121
     * &nbsp &nbsp If there is no <i>double buffer</i>:<br>
1122
     * &nbsp &nbsp &nbsp If there is a <i>behavior</i> for managing the
1123
     * <code>MapControl</code> instance, delegates the drawing process to that
1124
     * behavior, calling:
1125
     * <code><i>behavior_instance</i>.paintComponent(g)</code> &nbsp .<br>
1126
     * &nbsp &nbsp &nbsp Else, repaints the current graphical information
1127
     * quickly calling: <code>g.drawImage(image,0,0,null)</code> &nbsp .<br>
1128
     * &nbsp Else, (<i>status</i> is <i>OUTDATED</i>, or <i>ONLY_GRAPHICS</i>):
1129
     * executes a quickly repaint of the previous information calling
1130
     * <code>g.drawImage(image,0,0,null)</code>, and creates a <i>painting
1131
     * request</i> to delegate the heavy drawing process to the {@link Drawer2
1132
     * Drawer2}'s worker thread, according the <i>SingleWorketThread</i>
1133
     * pattern, starting a timer to update (invoking <code>repaint()</code> that
1134
     * comprises invoke this method) the view every delay of 360 ms. during the
1135
     * the process drawing.
1136
     * </p>
1137
     *
1138
     * @see javax.swing.JComponent#paintComponent(java.awt.Graphics)
1139
     * @see Drawer2
1140
     */
1141
    protected void paintComponent(Graphics g) {
1142
        adaptToImageSize();
1143

    
1144
        try {
1145
            mapControlDrawer.startDrawing(this);
1146
        } catch (InterruptedException e) {
1147
            LOG.info("Error locking the MapControlDrawer", e);
1148
        }
1149
        mapControlDrawer.setGraphics(g);
1150
        mapControlDrawer.stopDrawing(this);
1151
        mapControlDrawer.setViewPort(getMapContext().getViewPort());
1152

    
1153
        if (status == ACTUALIZADO) {
1154
            /*
1155
             * Si hay un behaviour y la imagen es distinta de null se delega el
1156
             * dibujado
1157
             * en dicho behaviour
1158
             */
1159
            if (image != null) {
1160
                if (currentMapTool != null) {
1161
                    currentMapTool.paintComponent(mapControlDrawer,true);
1162
                } else {
1163
                    mapControlDrawer.drawImage(image, 0, 0);
1164
                }
1165
            }
1166
                } else if ((status == DESACTUALIZADO)) {
1167
                        mapControlDrawer.drawImage(image, 0, 0);
1168
                        drawer.put(new PaintingRequest());
1169
                        timer.start();
1170
                }
1171
        cadgrid.drawGrid(mapControlDrawer);
1172
        drawCursor();
1173
    }
1174

    
1175
    private String getStatusLabel(int status ) {
1176
        switch(status) {
1177
            case ACTUALIZADO:
1178
                return "ACTUALIZADO";
1179
            case DESACTUALIZADO:
1180
                return "DESACTUALIZADO";
1181
            default:
1182
                return Integer.toString(status);
1183
        }
1184
    }
1185
    /**
1186
     * <p>
1187
     * Gets the {@link BufferedImage BufferedImage} used to accelerate the draw
1188
     * of new ''frames'' with changes, or new graphical items in this component.
1189
     * </p>
1190
     *
1191
     * @return double buffered image used by this component to accelerate the
1192
     *         draw of its graphical information, or <code>null</code> if isn't
1193
     *         already created
1194
     *
1195
     * @see BufferedImage
1196
     */
1197
    public BufferedImage getImage() {
1198
        return image;
1199
    }
1200

    
1201
    /**
1202
     * <p>
1203
     * Forces repaint all visible graphical information in this component.
1204
     * </p>
1205
     *
1206
     * <p>
1207
     * If <code>doClear == true</code>, before repainting, clears the background
1208
     * color, with the inner viewport's background color.
1209
     * </p>
1210
     *
1211
     * @param doClear
1212
     *            <code>true</code> if needs clearing the background color
1213
     *            before drawing the map
1214
     *
1215
     * @see #cancelDrawing()
1216
     * @see FLayers#setDirty(boolean)
1217
     */
1218
    public void drawMap(boolean doClear) {
1219
        cancelDrawing();
1220
        status = DESACTUALIZADO;
1221
        if (doClear) {
1222
            clearImage(image);
1223
        }
1224
        repaint();
1225
        revalidate();
1226
    }
1227

    
1228
    /**
1229
     * <p>
1230
     * Cancels any current drawing process, changing the status to
1231
     * <code>OUTDATED</code>, and forcing repaint only the layers dirty.
1232
     * </p>
1233
     *
1234
     * @see #cancelDrawing()
1235
     */
1236
    public void rePaintDirtyLayers() {
1237
        cancelDrawing();
1238
        status = DESACTUALIZADO;
1239
        repaint();
1240
    }
1241

    
1242
    /**
1243
     * @deprecated use {@link #drawMap(boolean)} instead, or even
1244
     * better {@link #getMapContext()}.invalidate().
1245
     */
1246
    public void drawGraphics() {
1247
        drawMap(false);
1248
    }
1249

    
1250
    /**
1251
     * @see java.awt.event.ComponentListener#componentHidden(java.awt.event.ComponentEvent)
1252
     */
1253
    public void componentHidden(ComponentEvent e) {
1254
    }
1255

    
1256
    /**
1257
     * @see java.awt.event.ComponentListener#componentMoved(java.awt.event.ComponentEvent)
1258
     */
1259
    public void componentMoved(ComponentEvent e) {
1260
    }
1261

    
1262
    /**
1263
     * @see java.awt.event.ComponentListener#componentResized(java.awt.event.ComponentEvent)
1264
     */
1265
    public void componentResized(ComponentEvent e) {
1266
        /*
1267
         * image = new BufferedImage(this.getWidth(), this.getHeight(),
1268
         * BufferedImage.TYPE_INT_ARGB);
1269
         * Graphics gTemp = image.createGraphics();
1270
         * gTemp.setColor(vp.getBackColor());
1271
         * gTemp.fillRect(0,0,getWidth(), getHeight());
1272
         * System.out.println("MapControl resized");
1273
         * // image = null;
1274
         * vp.setImageSize(new Dimension(getWidth(), getHeight()));
1275
         * getMapContext().getViewPort().setScale();
1276
         */
1277
        // drawMap(true);
1278
    }
1279

    
1280
    /**
1281
     * @see java.awt.event.ComponentListener#componentShown(java.awt.event.ComponentEvent)
1282
     */
1283
    public void componentShown(ComponentEvent e) {
1284
    }
1285

    
1286
    /**
1287
     * @see ExceptionHandlingSupport#addExceptionListener(ExceptionListener)
1288
     */
1289
    public void addExceptionListener(ExceptionListener o) {
1290
        exceptionHandlingSupport.addExceptionListener(o);
1291
    }
1292

    
1293
    /**
1294
     * @see ExceptionHandlingSupport#removeExceptionListener(ExceptionListener)
1295
     */
1296
    public boolean removeExceptionListener(ExceptionListener o) {
1297
        return exceptionHandlingSupport.removeExceptionListener(o);
1298
    }
1299

    
1300
    /**
1301
     * @see ExceptionHandlingSupport#throwException(Throwable)
1302
     */
1303
    protected void throwException(Throwable t) {
1304
        exceptionHandlingSupport.throwException(t);
1305
    }
1306

    
1307
    /**
1308
     * <p>
1309
     * Represents each <code>MapControl</code>'s data painting request.
1310
     * </p>
1311
     *
1312
     * <p>
1313
     * The request will be attended by a <code>Drawer2</code>, which will hold
1314
     * it since the <code>Drawer2</code>'s worker takes it, or arrives a new
1315
     * painting request, which will replace it.
1316
     * </p>
1317
     */
1318
    private class PaintingRequest {
1319

    
1320
        /**
1321
         * <p>
1322
         * Creates a new <code>PaintingRequest
1323
         * </p>
1324
         * instance.</p>
1325
         */
1326
        public PaintingRequest() {
1327
        }
1328

    
1329
        /**
1330
         * <p>
1331
         * <code>MapControl</code> paint process:
1332
         * </p>
1333
         *
1334
         * <p>
1335
         * <ul>
1336
         * <li><i>1.- </i>Cancels all previous <code>MapControl</code>'s drawing
1337
         * processes.</li>
1338
         * <li><i>2.- </i>If <i>status</i> was OUTDATED:
1339
         * <ul>
1340
         * <li><i>2.1.- </i>Fills the background color with viewport's
1341
         * background color, or <i>white</i> if it was undefined.</li>
1342
         * <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>
1343
         * .</li>
1344
         * <li><i>2.3.- </i>If <code>canceldraw.isCanceled()</code>
1345
         * <ul>
1346
         * <li><i>2.3.1.- </i>Sets <i>status</i> to OUTDATED.</li>
1347
         * <li><i>2.3.2.- </i>Sets <i>dirty</i> all layers stored in
1348
         * <i>MapContext</i>.</li>
1349
         * </ul>
1350
         * </li>
1351
         * <li><i>2.4.- </i>Else, sets <i>status</i> to UPDATED.</li>
1352
         * </ul>
1353
         * </li>
1354
         * <li><i>3.- </i>Stops the <i>timer</i>.</li>
1355
         * <li><i>4.- </i>Repaints this component invoking:
1356
         * <code>repaint();</code></li>
1357
         * </ul>
1358
         * </p>
1359
         *
1360
         * @see #cancelDrawing()
1361
         * @see MapContext#draw(BufferedImage, Graphics2D, Cancellable, double)
1362
         * @see MapContext#drawGraphics(BufferedImage, Graphics2D, Cancellable,
1363
         *      double)
1364
         *
1365
         * @see ViewPort
1366
         */
1367
        public void paint() {
1368
            try {
1369
                canceldraw.setCanceled(false);
1370
                if (status == DESACTUALIZADO) {
1371
                    BufferedImage drawImage = image;
1372
                    if( paintMode == PAINT_WHEN_DRAW_LAYERS_IS_COMPLETED ) {
1373
                        drawImage = new BufferedImage(
1374
                            image.getWidth(),
1375
                            image.getHeight(),
1376
                            BufferedImage.TYPE_INT_ARGB
1377
                        );
1378
                    }
1379
                    clearImage(drawImage);
1380
                    Graphics2D g = drawImage.createGraphics();
1381
                    mapContext.draw(drawImage, g, canceldraw, mapContext.getScaleView());
1382
                    g.dispose();
1383
                    image = drawImage;
1384
                    if (!canceldraw.isCanceled()) {
1385
                        status = ACTUALIZADO;
1386
                    }
1387
                                }
1388
                timer.stop();
1389
                repaint();
1390

    
1391
            } catch (Throwable e) {
1392
                timer.stop();
1393
                LOG.warn("Problems drawing mapcontext.",e);
1394
                throwException(e);
1395
            }
1396
        }
1397
    }
1398

    
1399
    /**
1400
     * <p>
1401
     * An instance of <code>Drawer2</code> could manage all
1402
     * <code>MapControl</code> painting requests.
1403
     * </p>
1404
     *
1405
     * <p>
1406
     * Based on the <i>WorkerThread</i> software pattern, creates a worker
1407
     * thread that will attend sequentially the current waiting painting
1408
     * request, after finishing the previous (that could be by a cancel action).
1409
     * </p>
1410
     *
1411
     * <p>
1412
     * All new {@link PaintingRequest PaintingRequest} generated will be stored
1413
     * as <i>waiting requests</i> since the worker attends it.
1414
     * </p>
1415
     *
1416
     * <p>
1417
     * If a worker finished and there was no <i>painting request</i>, the worker
1418
     * would be set to wait until any <i>painting request</i> would be put.
1419
     * </p>
1420
     *
1421
     * @author fjp
1422
     */
1423
    public class Drawer {
1424

    
1425
        // Una mini cola de 2. No acumulamos peticiones de dibujado
1426
        // dibujamos solo lo �ltimo que nos han pedido.
1427

    
1428
        /**
1429
         * <p>
1430
         * Painting request that's been attended by the <code>Drawer2</code>'s
1431
         * worker.
1432
         * </p>
1433
         *
1434
         * @see #put(org.gvsig.fmap.mapcontrol.MapControl.PaintingRequest)
1435
         * @see #take()
1436
         */
1437
        private PaintingRequest paintingRequest;
1438

    
1439
        /**
1440
         * <p>
1441
         * Painting request waiting to be attended by the <code>Drawer2</code>'s
1442
         * worker.
1443
         * </p>
1444
         *
1445
         * @see #put(org.gvsig.fmap.mapcontrol.MapControl.PaintingRequest)
1446
         * @see #take()
1447
         */
1448
        private PaintingRequest waitingRequest;
1449

    
1450
        /**
1451
         * <p>
1452
         * Determines that the <code>Drawer2</code>'s worker is busy attending a
1453
         * painting request.
1454
         * </p>
1455
         *
1456
         * @see #put(org.gvsig.fmap.mapcontrol.MapControl.PaintingRequest)
1457
         * @see #take()
1458
         */
1459
        private boolean waiting;
1460

    
1461
        /**
1462
         * <p>
1463
         * Notifies the <code>Drawer2</code>'s worker to finish or continue with
1464
         * its process.
1465
         * </p>
1466
         *
1467
         * @see #setShutdown(boolean)
1468
         */
1469
        private boolean shutdown;
1470

    
1471
                private Thread worker;
1472

    
1473
        /**
1474
         * <p>
1475
         * Sets this <code>Drawer2</code>'s worker to finish or continue with
1476
         * its process.
1477
         * </p>
1478
         *
1479
         * @param isShutdown
1480
         *            a boolean value
1481
         */
1482
        public void setShutdown(boolean isShutdown) {
1483
            shutdown = isShutdown;
1484
            if (shutdown) {
1485
                    worker.interrupt();
1486
            }
1487
        }
1488

    
1489
        /**
1490
         * <p>
1491
         * Creates a new drawer for managing all data painting requests in
1492
         * <code>MapControl</code>.
1493
         * </p>
1494
         *
1495
         * <p>
1496
         * Includes the following steps:
1497
         * <ul>
1498
         * <li>By default, there is no <i>current painting request</i>.</li>
1499
         * <li>By default, there is no <i>waiting painting request</i>.</li>
1500
         * <li>By default, the worker thread is waiting no <i>painting
1501
         * request</i>.</li>
1502
         * <li>By default, the worker thread is running.</li>
1503
         * <li>Creates and starts a worker thread for attending the <i>painting
1504
         * requests</i>.</li>
1505
         * </ul>
1506
         * </p>
1507
         */
1508
        public Drawer() {
1509
            paintingRequest = null;
1510
            waitingRequest = null;
1511
            waiting = false;
1512
            shutdown = false;
1513
            worker = new Thread(new Worker(), "MapControl Drawer Worker");
1514
            worker.start();
1515
        }
1516

    
1517
        /**
1518
         * <p>
1519
         * Sets a <code>PaintingRequest</code> to be attended by the worker
1520
         * thread of this object. If this one was waiting, wakes up.
1521
         * </p>
1522
         *
1523
         * <p>
1524
         * All waiting threads will be notified synchronized.
1525
         * </p>
1526
         *
1527
         * @param newPaintRequest
1528
         *
1529
         * @see #take()
1530
         */
1531
        public void put(PaintingRequest newPaintRequest) {
1532
            waitingRequest = newPaintRequest;
1533
            if (waiting) {
1534
                synchronized (this) {
1535
                    notifyAll();
1536
                }
1537
            }
1538
        }
1539

    
1540
        /**
1541
         * <p>
1542
         * Used by this object's worker, returns the current waiting drawing
1543
         * request, causing current thread to wait until another thread invokes
1544
         * {@link #put(org.gvsig.fmap.mapcontrol.MapControl.PaintingRequest)
1545
         * #put(com.iver.cit.gvsig.fmap.MapControl.PaintingRequest)}, if there
1546
         * was no waiting request.
1547
         * </p>
1548
         *
1549
         * <p>
1550
         * All threads will access synchronized to the waiting request.
1551
         * </p>
1552
         *
1553
         * @return <code>PaintingRequest</code> that was waiting to be attended
1554
         *
1555
         * @see #put(org.gvsig.fmap.mapcontrol.MapControl.PaintingRequest)
1556
         */
1557
        public PaintingRequest take() {
1558
            if (waitingRequest == null) {
1559
                synchronized (this) {
1560
                    waiting = true;
1561
                    try {
1562
                        wait();
1563
                    } catch (InterruptedException ie) {
1564
                        waiting = false;
1565
                    }
1566
                }
1567
            }
1568
            paintingRequest = waitingRequest;
1569
            waitingRequest = null;
1570
            return paintingRequest;
1571
        }
1572

    
1573
        /**
1574
         * <p>
1575
         * Thread for attending painting requests.
1576
         * </p>
1577
         *
1578
         * <p>
1579
         * If there was no double buffer, sets the status to
1580
         * <code>OUTDATED</code> and finishes, otherwise takes the painting
1581
         * request (it's probably that would wait some time), cancel the
1582
         * previous drawing process, and starts processing the request.
1583
         * </p>
1584
         *
1585
         * @see Thread
1586
         */
1587
        private class Worker implements Runnable {
1588

    
1589
            /*
1590
             * (non-Javadoc)
1591
             *
1592
             * @see java.lang.Runnable#run()
1593
             */
1594
            public void run() {
1595
                while (!shutdown) {
1596
                    PaintingRequest p = take();
1597
                    // System.out.println("Pintando");
1598
                    if (image != null) {
1599
                        cancelDrawing();
1600
                        if (p != null) {
1601
                                p.paint();
1602
                        }
1603
                    } else {
1604
                        status = DESACTUALIZADO;
1605
                    }
1606
                }
1607
            }
1608
        }
1609
    }
1610

    
1611
    /**
1612
     * <p>
1613
     * An instance of <code>CancelDraw</code> will be shared by all this
1614
     * <code>MapControl</code>'s <code>MapContext</code> layers, allowing
1615
     * receive a notification that, when they're been drawn, to be cancelled.
1616
     * </p>
1617
     *
1618
     * @see Cancellable
1619
     *
1620
     * @author Fernando Gonz�lez Cort�s
1621
     */
1622
    public class CancelDraw implements Cancellable {
1623

    
1624
        /**
1625
         * <p>
1626
         * Determines if the drawing task must be canceled or not.
1627
         * </p>
1628
         *
1629
         * @see #isCanceled()
1630
         * @see #setCanceled(boolean)
1631
         */
1632
        private boolean cancel = false;
1633

    
1634
        /**
1635
         * Creates a new <code>CancelDraw</code> object.
1636
         */
1637
        public CancelDraw() {
1638
        }
1639

    
1640
        /*
1641
         * (non-Javadoc)
1642
         *
1643
         * @see com.iver.utiles.swing.threads.Cancellable#setCanceled(boolean)
1644
         */
1645
        public void setCanceled(boolean b) {
1646
            cancel = b;
1647
        }
1648

    
1649
        /*
1650
         * (non-Javadoc)
1651
         *
1652
         * @see com.iver.utiles.swing.threads.Cancellable#isCanceled()
1653
         */
1654
        public boolean isCanceled() {
1655
            return cancel;
1656
        }
1657
    }
1658

    
1659
    /**
1660
     * <p>
1661
     * Listens all kind of mouse events produced in {@link MapControl
1662
     * MapControl}, and invokes its current map tool <i>(
1663
     * {@link MapControl#getCurrentMapTool() MapControl#getCurrentMapTool()}</i>
1664
     * to simulate a behavior.
1665
     * </p>
1666
     *
1667
     * <p>
1668
     * Mouse wheel moved events produce a <i>zoom in</i> operation if wheel
1669
     * rotation is negative, or a <i>zoom out</i> if its positive. Both will be
1670
     * centered in the position of the mouse, but, meanwhile <i>zoom in</i>
1671
     * operation applies a factor of 0.9, <i>zoom out</i> operation applies a
1672
     * factor of 1.2
1673
     * </p>
1674
     *
1675
     * <p>
1676
     * Mouse wheel moved events can be produced as much frequently, that between
1677
     * each one, the drawing process could hadn't finished. This is the reason
1678
     * that, in this situation, cancels always the previous drawing process
1679
     * before applying a <i>zoom</i> operation, and ignores all new mouse
1680
     * positions that are produced before 1 second.
1681
     * </p>
1682
     *
1683
     * @author Fernando Gonz�lez Cort�s
1684
     */
1685
    public class MapToolListener implements MouseListener, MouseWheelListener,
1686
        MouseMotionListener {
1687

    
1688
        /**
1689
         * <p>
1690
         * Used to avoid mouse wheel move events closed.
1691
         * </p>
1692
         *
1693
         * <p>
1694
         * If a mouse wheel move event is produced
1695
         */
1696
        long t1;
1697

    
1698
        /**
1699
         * <p>
1700
         * Position of the mouse, in map coordinates.
1701
         * </p>
1702
         *
1703
         * <p>
1704
         * This point coordinates will be used as center of the <i>zoom</i>
1705
         * operation.
1706
         * </p>
1707
         */
1708
        Point2D pReal;
1709

    
1710
        /**
1711
         * @see java.awt.event.MouseListener#mouseClicked(java.awt.event.MouseEvent)
1712
         * @see Behavior#mouseClicked(MouseEvent)
1713
         */
1714
        public void mouseClicked(MouseEvent e) {
1715
            try {
1716
                if (currentMapTool != null) {
1717
                    if (mapAdjustedPoint!=null) {
1718
                        Point2D mp = vp.fromMapPoint(mapAdjustedPoint);
1719
                        e.translatePoint((int) mp.getX() - e.getX(), 
1720
                                (int) mp.getY()- e.getY());
1721
                    }
1722
                    currentMapTool.mouseClicked(e);
1723
                }
1724
                Point2D p;
1725

    
1726
                if (mapAdjustedPoint != null) {
1727
                    p = mapAdjustedPoint;
1728
                } else {
1729
                    p = vp.toMapPoint(adjustedPoint);
1730
                }
1731
                previousPoint = new double[] { p.getX(), p.getY() };
1732
            } catch (BehaviorException t) {
1733
                throwException(t);
1734
            }
1735
        }
1736

    
1737
        /**
1738
         * @see java.awt.event.MouseListener#mouseEntered(java.awt.event.MouseEvent)
1739
         * @see Behavior#mouseEntered(MouseEvent)
1740
         */
1741
        public void mouseEntered(MouseEvent e) {
1742
            setToolMouse();
1743
            try {
1744
                if (currentMapTool != null) {
1745
                    currentMapTool.mouseEntered(e);
1746
                }
1747
            } catch (BehaviorException t) {
1748
                throwException(t);
1749
            }
1750
        }
1751

    
1752
        /**
1753
         * @see java.awt.event.MouseListener#mouseExited(java.awt.event.MouseEvent)
1754
         * @see Behavior#mouseExited(MouseEvent)
1755
         */
1756
        public void mouseExited(MouseEvent e) {
1757
            try {
1758
                if (currentMapTool != null) {
1759
                    currentMapTool.mouseExited(e);
1760
                }
1761
            } catch (BehaviorException t) {
1762
                throwException(t);
1763
            }
1764
            // Remove the snapping image if exist
1765
            usedSnap = null;
1766
            repaint();
1767
        }
1768

    
1769
        /**
1770
         * @see java.awt.event.MouseListener#mousePressed(java.awt.event.MouseEvent)
1771
         * @see Behavior#mousePressed(MouseEvent)
1772
         */
1773
        public void mousePressed(MouseEvent e) {
1774
            try {
1775
                if (currentMapTool != null) {
1776
                    currentMapTool.mousePressed(e);
1777
                }
1778
            } catch (BehaviorException t) {
1779
                throwException(t);
1780
            }
1781
        }
1782

    
1783
        /**
1784
         * @see java.awt.event.MouseListener#mouseReleased(java.awt.event.MouseEvent)
1785
         * @see Behavior#mouseReleased(MouseEvent)
1786
         */
1787
        public void mouseReleased(MouseEvent e) {
1788
            try {
1789
                if (currentMapTool != null) {
1790
                    currentMapTool.mouseReleased(e);
1791
                }
1792
            } catch (BehaviorException t) {
1793
                throwException(t);
1794
            }
1795
        }
1796

    
1797
        /**
1798
         * @see java.awt.event.MouseWheelListener#mouseWheelMoved(java.awt.event.MouseWheelEvent)
1799
         * @see Behavior#mouseWheelMoved(MouseWheelEvent)
1800
         */
1801
        public void mouseWheelMoved(MouseWheelEvent e) {
1802
            try {
1803
                if (currentMapTool == null) {
1804
                    return;
1805
                }
1806

    
1807
                currentMapTool.mouseWheelMoved(e);
1808

    
1809
                // Si el tool actual no ha consumido el evento
1810
                // entendemos que quiere el comportamiento por defecto.
1811
                if (!e.isConsumed() && defaultMouseWheelEnabled ) {
1812
                    // Para usar el primer punto sobre el que queremos centrar
1813
                    // el mapa, dejamos pasar un segundo para considerar el
1814
                    // siguiente
1815
                    // punto como v�lido.
1816
                    if (t1 == 0) {
1817
                        t1 = System.currentTimeMillis();
1818
                        pReal = vp.toMapPoint(e.getPoint());
1819
                    } else {
1820
                        long t2 = System.currentTimeMillis();
1821
                        if ((t2 - t1) > 1000) {
1822
                            t1 = 0;
1823
                        }
1824
                    }
1825
                    cancelDrawing();
1826
                    ViewPort vp = getViewPort();
1827

    
1828
                    /*
1829
                     * Point2D pReal = new
1830
                     * Point2D.Double(vp.getAdjustedExtent().getCenterX(),
1831
                     * vp.getAdjustedExtent().getCenterY());
1832
                     */
1833
                    int amount = e.getWheelRotation();
1834
                    double nuevoX;
1835
                    double nuevoY;
1836
                    double factor;
1837

    
1838
                    if (amount < 0) // nos acercamos
1839
                    {
1840
                        factor = 0.9;
1841
                    } else // nos alejamos
1842
                    {
1843
                        factor = 1.2;
1844
                    }
1845
                    if (vp.getExtent() != null) {
1846
                        nuevoX =
1847
                            pReal.getX()
1848
                                - ((vp.getExtent().getWidth() * factor) / 2.0);
1849
                        nuevoY =
1850
                            pReal.getY()
1851
                                - ((vp.getExtent().getHeight() * factor) / 2.0);
1852
                        double x = nuevoX;
1853
                        double y = nuevoY;
1854
                        double width = vp.getExtent().getWidth() * factor;
1855
                        double height = vp.getExtent().getHeight() * factor;
1856

    
1857
                        try {
1858
                            vp.setEnvelope(geomManager.createEnvelope(x, y, x
1859
                                + width, y + height, SUBTYPES.GEOM2D));
1860
                        } catch (CreateEnvelopeException e1) {
1861
                            LOG.info("Error creating the envelope", e);
1862
                        }
1863
                    }
1864

    
1865
                }
1866
            } catch (BehaviorException t) {
1867
                throwException(t);
1868
            }
1869
        }
1870

    
1871
        /**
1872
         * @see java.awt.event.MouseMotionListener#mouseDragged(java.awt.event.MouseEvent)
1873
         * @see Behavior#mouseDragged(MouseEvent)
1874
         */
1875
        public void mouseDragged(MouseEvent e) {
1876
            calculateSnapPoint(e.getPoint());
1877
            try {
1878
                if (currentMapTool != null) {
1879
                    currentMapTool.mouseDragged(e);
1880
                }
1881
            } catch (BehaviorException t) {
1882
                throwException(t);
1883
            }
1884
            repaint();
1885
        }
1886

    
1887
        /**
1888
         * @see java.awt.event.MouseMotionListener#mouseMoved(java.awt.event.MouseEvent)
1889
         * @see Behavior#mouseMoved(MouseEvent)
1890
         */
1891
        public void mouseMoved(MouseEvent e) {
1892
            calculateSnapPoint(e.getPoint());
1893
            try {
1894
                if (currentMapTool != null) {
1895
                    currentMapTool.mouseMoved(e);
1896
                }
1897
            } catch (BehaviorException t) {
1898
                throwException(t);
1899
            }
1900
            repaint();
1901
        }
1902
    }
1903

    
1904
    /**
1905
     * <p<code>MapContextListener</code> listens all events produced in a
1906
     * <code>MapControl</code>'s <code>MapContext</code> object during an atomic
1907
     * period of time, and sets it to dirty, <i>executing
1908
     * <code>drawMap(false)</code>, if any of the
1909
     * following conditions is accomplished</i>:
1910
     * <ul>
1911
     * <li>Any of the <code>LayerEvent</code> in the <code>AtomicEvent</code>
1912
     * parameter notifies a <i>visibility change</i>.</li>
1913
     * <li>There is at least one <code>ColorEvent</code> in the
1914
     * <code>AtomicEvent</code> parameter.</li>
1915
     * <li>There is at least one <code>ExtentEvent</code> in the
1916
     * <code>AtomicEvent</code> parameter.</li>
1917
     * <li>Any of the <code>LayerCollectionEvent</code> in the
1918
     * <code>AtomicEvent</code> parameter notifies that a driver's layer has
1919
     * reloaded it successfully.</li>
1920
     * <li>There is at least one <code>LegendEvent</code> in the
1921
     * <code>AtomicEvent</code> parameter.</li>
1922
     * <li>There is at least one <code>SelectionEvent</code> in the
1923
     * <code>AtomicEvent</code> parameter.</li>
1924
     * </ul>
1925
     * </p>
1926
     *
1927
     * @author Fernando Gonz�lez Cort�s
1928
     */
1929
    public class MapContextListener implements AtomicEventListener {
1930

    
1931
        /**
1932
         * @see org.gvsig.fmap.mapcontext.events.listeners.AtomicEventListener#atomicEvent(org.gvsig.fmap.mapcontext.events.AtomicEvent)
1933
         */
1934
        public void atomicEvent(final AtomicEvent e) {
1935
            if (!SwingUtilities.isEventDispatchThread()) {
1936
                SwingUtilities.invokeLater(new Runnable() {
1937

    
1938
                    public void run() {
1939
                        atomicEvent(e);
1940
                    }
1941
                });
1942
                return;
1943
            }
1944
            LayerEvent[] layerEvents = e.getLayerEvents();
1945

    
1946
            for (int i = 0; i < layerEvents.length; i++) {
1947
                if (layerEvents[i].getProperty().equals("visible")) {
1948
                    drawMap(false);
1949
                    return;
1950
                } else
1951
                    if (layerEvents[i].getEventType() == LayerEvent.EDITION_CHANGED) {
1952
                        drawMap(false);
1953
                        return;
1954
                    }
1955
            }
1956

    
1957
            if (e.getColorEvents().length > 0) {
1958
                drawMap(false);
1959
                return;
1960
            }
1961

    
1962
            if (e.getExtentEvents().length > 0) {
1963
                drawMap(false);
1964
                return;
1965
            }
1966

    
1967
            if (e.getProjectionEvents().length > 0) {
1968
                // redraw = true;
1969
            }
1970

    
1971
            LayerCollectionEvent[] aux = e.getLayerCollectionEvents();
1972
            if (aux.length > 0) {
1973
                for (int i = 0; i < aux.length; i++) {
1974
                    if (aux[i].getAffectedLayer().getFLayerStatus()
1975
                        .isDriverLoaded()) {
1976
                        drawMap(false);
1977
                        return;
1978
                    }
1979
                }
1980

    
1981
            }
1982

    
1983
            if (e.getLegendEvents().length > 0) {
1984
                drawMap(false);
1985
                return;
1986
            }
1987

    
1988
            if (e.getSelectionEvents().length > 0) {
1989
                drawMap(false);
1990
                return;
1991
            }
1992
        }
1993
    }
1994

    
1995
    /**
1996
     * <p>
1997
     * Gets the <code>ViewPort</code> of this component's {@link MapContext
1998
     * MapContext} .
1999
     * </p>
2000
     *
2001
     * @see MapContext#getViewPort()
2002
     */
2003
    public ViewPort getViewPort() {
2004
        return vp;
2005
    }
2006

    
2007
    /**
2008
     * <p>
2009
     * Returns all registered <code>Behavior</code> that can define a way to
2010
     * work with this <code>MapControl</code>.
2011
     * </p>
2012
     *
2013
     * @return registered <code>Behavior</code> to this <code>MapControl</code>
2014
     *
2015
     * @see #addBehavior(String, Behavior)
2016
     * @see #addBehavior(String, Behavior[])
2017
     * @see #getMapToolsKeySet()
2018
     * @see #hasTool(String)
2019
     */
2020
    public HashMap getNamesMapTools() {
2021
        return (HashMap) namesMapTools;
2022
    }
2023

    
2024
    /*
2025
     * (non-Javadoc)
2026
     *
2027
     * @see
2028
     * com.iver.cit.gvsig.fmap.edition.commands.CommandListener#commandRepaint()
2029
     */
2030
    public void commandRepaint() {
2031
        drawMap(false);
2032
    }
2033

    
2034
    /*
2035
     * (non-Javadoc)
2036
     *
2037
     * @see
2038
     * com.iver.cit.gvsig.fmap.edition.commands.CommandListener#commandRefresh()
2039
     */
2040
    public void commandRefresh() {
2041
        // TODO Auto-generated method stub
2042
    }
2043

    
2044
    /**
2045
     * <p>
2046
     * Equivalent operation to <i>undo</i>.
2047
     * </p>
2048
     *
2049
     * <p>
2050
     * Exchanges the previous tool with the current one.
2051
     * </p>
2052
     *
2053
     * @see #addBehavior(String, Behavior)
2054
     * @see #addBehavior(String, Behavior[])
2055
     * @see #setTool(String)
2056
     */
2057
    public void setPrevTool() {
2058
        setTool(prevTool);
2059
    }
2060

    
2061
    /**
2062
     * <p>
2063
     * Executes a <i>zoom in</i> operation centered at the center of the extent.
2064
     * </p>
2065
     *
2066
     * <p>
2067
     * This implementation is designed for being invoked outside a
2068
     * <code>Behavior</code>, for example by an action of pressing a button; and
2069
     * simulates that the event has been produced by releasing the <i>button
2070
     * 1</i> of the mouse using the registered <code>Behavior</code> in this
2071
     * <code>MapControl</code> object that's responsible for the <i>zoom in</i>
2072
     * operation.
2073
     * </p>
2074
     *
2075
     * @see #zoomOut()
2076
     */
2077
    public void zoomIn() {
2078
        Behavior mapTool = (Behavior) namesMapTools.get("zoomIn");
2079
        ViewPort vp = getViewPort();
2080
        Envelope r = getViewPort().getAdjustedExtent();
2081
        Point2D pCenter = vp.fromMapPoint(r.getCenter(0), r.getCenter(1));
2082
        MouseEvent e =
2083
            new MouseEvent(this, MouseEvent.MOUSE_RELEASED,
2084
                MouseEvent.ACTION_EVENT_MASK, MouseEvent.BUTTON1_MASK, (int) pCenter
2085
                    .getX(), (int) pCenter.getY(), 1, true, MouseEvent.BUTTON1);
2086
        try {
2087
            mapTool.mousePressed(e);
2088
            mapTool.mouseReleased(e);
2089
        } catch (BehaviorException t) {
2090
            throwException(t);
2091
        }
2092
    }
2093

    
2094
    /**
2095
     * <p>
2096
     * Executes a <i>zoom out</i> operation centered at the center of the
2097
     * extent.
2098
     * </p>
2099
     *
2100
     * <p>
2101
     * This implementation is thought for being invoked outside a
2102
     * <code>Behavior</code>, for example by an action of pressing a button, and
2103
     * simulates that the event has been produced by releasing the <i>button
2104
     * 1</i> of the mouse using the registered <code>Behavior</code> in this
2105
     * <code>MapControl</code> object that's responsible for the <i>zoom out</i>
2106
     * operation.
2107
     * </p>
2108
     *
2109
     * @see #zoomIn()
2110
     */
2111
    public void zoomOut() {
2112
        Behavior mapTool = (Behavior) namesMapTools.get("zoomOut");
2113
        ViewPort vp = getViewPort();
2114
        Envelope r = getViewPort().getAdjustedExtent();
2115
        Point2D pCenter = vp.fromMapPoint(r.getCenter(0), r.getCenter(1));
2116
        MouseEvent e =
2117
            new MouseEvent(this, MouseEvent.MOUSE_RELEASED,
2118
                MouseEvent.ACTION_EVENT_MASK, MouseEvent.BUTTON1_MASK, (int) pCenter
2119
                    .getX(), (int) pCenter.getY(), 1, true, MouseEvent.BUTTON1);
2120
        try {
2121
            mapTool.mousePressed(e);
2122
            mapTool.mouseReleased(e);
2123
        } catch (BehaviorException t) {
2124
            throwException(t);
2125
        }
2126
    }
2127

    
2128
    /**
2129
     * <p>
2130
     * Returns the listener used to catch all mouse events produced in this
2131
     * <code>MapControl</code> instance and that redirects the calls to the
2132
     * current map tool.
2133
     * </p>
2134
     *
2135
     * @return the map tool listener used
2136
     */
2137
    public MapToolListener getMapToolListener() {
2138
        return mapToolListener;
2139
    }
2140

    
2141
    // mapTool can be null, for instance, in 3D's navigation tools
2142
    /**
2143
     * <p>
2144
     * Sets <code>mapTool</code> as this <code>MapControl</code>'s current map
2145
     * tool.
2146
     *
2147
     * @param mapTool
2148
     *            a map tool, or <code>null</code> to disable the interaction
2149
     *            with the user
2150
     *
2151
     * @see #getCurrentMapTool()
2152
     * @see #getCurrentTool()
2153
     * @see #setTool(String)
2154
     * @see #setPrevTool()
2155
     * @see #addBehavior(String, Behavior)
2156
     * @see #addBehavior(String, Behavior[])
2157
     */
2158
    public void setCurrentMapTool(Behavior mapTool) {
2159
        currentMapTool = mapTool;
2160
    }
2161

    
2162
    /**
2163
     * <p>
2164
     * Sets the delay to the timer that refreshes this <code>MapControl</code>
2165
     * instance.
2166
     * </p>
2167
     *
2168
     * <p>
2169
     * <code>Delay (in ms) = 1000 / getDrawFrameRate()</code>
2170
     * </p>
2171
     *
2172
     * @see #getDrawFrameRate()
2173
     * @see #setDrawFrameRate(int)
2174
     */
2175
    public void applyFrameRate() {
2176
        if (MapContext.getDrawFrameRate() > 0) {
2177
            timer.setDelay(1000 / MapContext.getDrawFrameRate());
2178
        }
2179
    }
2180

    
2181
    /**
2182
     * <p>
2183
     * Determines if its enabled the repaint that invokes the timer according to
2184
     * {@link #getDrawFrameRate() #getDrawFrameRate()}.
2185
     * </p>
2186
     *
2187
     * @return <code>true</code> if its enabled; otherwise <code>false</code>
2188
     */
2189
    public static boolean isDrawAnimationEnabled() {
2190
        return drawAnimationEnabled;
2191
    }
2192

    
2193
    /**
2194
     * <p>
2195
     * Sets if its enabled the repaint that invokes the timer according to
2196
     * {@link #getDrawFrameRate() #getDrawFrameRate()}.
2197
     * </p>
2198
     *
2199
     * @param drawAnimationEnabled
2200
     *            <code>true</code> to enable the mode; otherwise
2201
     *            <code>false</code>
2202
     */
2203
    public static void setDrawAnimationEnabled(boolean drawAnimationEnabled) {
2204
        MapControl.drawAnimationEnabled = drawAnimationEnabled;
2205
    }
2206

    
2207
    /**
2208
     * <p>
2209
     * Gets the shared object that determines if a drawing process must be
2210
     * cancelled or can continue.
2211
     * </p>
2212
     *
2213
     * @return the shared object that determines if a drawing process must be
2214
     *         cancelled or can continue
2215
     */
2216
    public CancelDraw getCanceldraw() {
2217
        return canceldraw;
2218
    }
2219

    
2220
    /**
2221
     * <p>
2222
     * Gets the tool used in combination with the current tool of this
2223
     * <code>MapControl</code>.
2224
     * </p>
2225
     *
2226
     * @return the tool used in combination with the <code>currentMapTool</code>
2227
     *         ; <code>null</code> if there is
2228
     *         no combined tool
2229
     */
2230
    public Behavior getCombinedTool() {
2231
        return combinedTool;
2232
    }
2233

    
2234
    /**
2235
     * <p>
2236
     * Sets a tool to be used in combination with the current tool of this
2237
     * <code>MapControl</code>.
2238
     * </p>
2239
     *
2240
     * @param combinedTool
2241
     *            a tool to be used in combination with the current tool of
2242
     *            <code>MapControl</code>
2243
     */
2244
    public void setCombinedTool(Behavior combinedTool) {
2245
        this.combinedTool = combinedTool;
2246

    
2247
        if (currentMapTool == null) {
2248
            return;
2249
        }
2250

    
2251
        if (currentMapTool instanceof CompoundBehavior) {
2252
            ((CompoundBehavior) currentMapTool).addMapBehavior(combinedTool,
2253
                true);
2254
        } else {
2255
            currentMapTool =
2256
                new CompoundBehavior(new Behavior[] { currentMapTool });
2257
            ((CompoundBehavior) currentMapTool).addMapBehavior(combinedTool,
2258
                true);
2259
        }
2260

    
2261
    }
2262

    
2263
    /**
2264
     * <p>
2265
     * Adds a new tool as combined tool.
2266
     * </p>
2267
     * <p>
2268
     * The new tool will be stored with the previous combined tools, and will be
2269
     * combined with the current tool.
2270
     * </p>
2271
     * <p>
2272
     * If <code>tool</code> was already stored as a combined tool, doesn't adds
2273
     * it.
2274
     * </p>
2275
     *
2276
     * @param tool
2277
     *            a new tool to be used combined with the current tool
2278
     */
2279
    public void addCombinedBehavior(Behavior tool) {
2280
        tool.setMapControl(this);
2281
        if (combinedTool == null) {
2282
            combinedTool = tool;
2283
        } else {
2284
            if (combinedTool instanceof CompoundBehavior) {
2285
                if (((CompoundBehavior) combinedTool).containsBehavior(tool)) {
2286
                    return;
2287
                }
2288

    
2289
                ((CompoundBehavior) combinedTool).addMapBehavior(tool, true);
2290
            } else {
2291
                if (combinedTool.equals(tool)) {
2292
                    return;
2293
                }
2294

    
2295
                combinedTool =
2296
                    new CompoundBehavior(new Behavior[] { combinedTool });
2297
                ((CompoundBehavior) combinedTool).addMapBehavior(tool, true);
2298
                combinedTool.setMapControl(this);
2299
            }
2300
        }
2301

    
2302
        if (currentMapTool == null) {
2303
            return;
2304
        }
2305

    
2306
        if (currentMapTool instanceof CompoundBehavior) {
2307
            ((CompoundBehavior) currentMapTool).addMapBehavior(tool, true);
2308
        } else {
2309
            currentMapTool =
2310
                new CompoundBehavior(new Behavior[] { currentMapTool });
2311
            ((CompoundBehavior) currentMapTool).addMapBehavior(tool, true);
2312
        }
2313
    }
2314

    
2315
    /**
2316
     * <p>
2317
     * Removes the tool <code>tool</code> used in combination with the current
2318
     * tool of this <code>MapControl</code>.
2319
     * </p>
2320
     */
2321
    public void removeCombinedTool(Behavior tool) {
2322
        if ((currentMapTool != null)
2323
            && (currentMapTool instanceof CompoundBehavior)) {
2324
            ((CompoundBehavior) currentMapTool).removeMapBehavior(tool);
2325
        }
2326

    
2327
        if (combinedTool == null) {
2328
            return;
2329
        }
2330

    
2331
        if (combinedTool instanceof CompoundBehavior) {
2332
            ((CompoundBehavior) combinedTool).removeMapBehavior(tool);
2333
        } else {
2334
            combinedTool = null;
2335
        }
2336
    }
2337

    
2338
    /**
2339
     * <p>
2340
     * Updates the grid on the <code>ViewPort</code> of the associated
2341
     * <code>MapControl</code> object according the values in the
2342
     * {@link com.iver.cit.gvsig.gui.cad.CADToolAdapter.prefs.Preferences
2343
     * com.iver.cit.gvsig.gui.cad.CADToolAdapter.prefs.Preferences}.
2344
     * </p>
2345
     *
2346
     * <p>
2347
     * The preferences are:
2348
     * <ul>
2349
     * <li>Show/hide the grid.</li>
2350
     * <li>Adjust or not the grid.</li>
2351
     * <li>Horizontal ( X ) line separation.</li>
2352
     * <li>Vertical ( Y ) line separation.</li>
2353
     * </ul>
2354
     * </p>
2355
     */
2356
    public void initializeGrid() {
2357
        Preferences prefs = mapControlManager.getEditionPreferences();
2358
        boolean showGrid =
2359
            prefs.getBoolean("grid.showgrid", cadgrid.isShowGrid());
2360
        boolean adjustGrid =
2361
            prefs.getBoolean("grid.adjustgrid", cadgrid.isAdjustGrid());
2362

    
2363
        double dx = prefs.getDouble("grid.distancex", cadgrid.getGridSizeX());
2364
        double dy = prefs.getDouble("grid.distancey", cadgrid.getGridSizeY());
2365

    
2366
        setGridVisibility(showGrid);
2367
        setAdjustGrid(adjustGrid);
2368
        cadgrid.setGridSizeX(dx);
2369
        cadgrid.setGridSizeY(dy);
2370
    }
2371

    
2372
    public void setAdjustGrid(boolean adjustGrid) {
2373
        cadgrid.setAdjustGrid(adjustGrid);
2374
    }
2375

    
2376
    public void setGridVisibility(boolean showGrid) {
2377
        cadgrid.setShowGrid(showGrid);
2378
        cadgrid.setViewPort(getViewPort());
2379
        this.repaint();
2380
    }
2381

    
2382
    /**
2383
     * Uses like a mouse pointer the image that provides the
2384
     * selected tool.
2385
     *
2386
     */
2387

    
2388
    private Image lastImageCursor = null;
2389
    private Cursor lastCursor = null;
2390

    
2391
    private void setToolMouse() {
2392
        Image imageCursor = getCurrentMapTool().getImageCursor();
2393
        if (imageCursor != null && imageCursor == lastImageCursor && lastCursor != null && lastCursor != transparentCursor) {
2394
            setCursor(lastCursor);
2395
            return;
2396
        }
2397
        lastImageCursor = imageCursor;
2398
        Toolkit toolkit = Toolkit.getDefaultToolkit();
2399
        Cursor c = toolkit.createCustomCursor(imageCursor, new Point(16, 16), "img");
2400
        setCursor(c);
2401
        lastCursor = c;
2402
    }
2403

    
2404
    /**
2405
     * Makes the mouse pointer transparent.
2406
     */
2407
    private void setTransparentMouse() {
2408
        setCursor(transparentCursor);
2409
        lastCursor = transparentCursor;
2410
    }
2411

    
2412
    /**
2413
     * <p>
2414
     * Draws a 31x31 pixels cross round the mouse's cursor with an small
2415
     * geometry centered:
2416
     * <ul>
2417
     * <li><i>an square centered</i>: if isn't over a <i>control point</i>.
2418
     * <li><i>an small geometry centered according to the kind of control
2419
     * point</i>: if it's over a control point. In this case, the small geometry
2420
     * is drawn by a {@link ISnapper ISnapper} type object.<br>
2421
     * On the other hand, a light-yellowed background tool tip text with the
2422
     * type of <i>control point</i> will be displayed.</li>
2423
     * </p>
2424
     *
2425
     * @param g
2426
     *            <code>MapControl</code>'s graphics where the data will be
2427
     *            drawn
2428
     */
2429
    private void drawCursor() {
2430
        if (adjustedPoint == null) {
2431
            return;
2432
        }
2433

    
2434
        if (usedSnap != null) {
2435
            usedSnap.draw(mapControlDrawer, adjustedPoint);
2436
            setTransparentMouse();
2437
        } else {
2438
            setToolMouse();
2439
        }
2440
    }
2441

    
2442
    /**
2443
     * <p>
2444
     * Adjusts the <code>point</code> to the grid if its enabled, and sets
2445
     * <code>mapHandlerAdjustedPoint</code> with that new value.
2446
     * </p>
2447
     *
2448
     * <p>
2449
     * The value returned is the distance between those points: the original and
2450
     * the adjusted one.
2451
     * </p>
2452
     *
2453
     * @param point
2454
     *            point to adjust
2455
     * @param mapHandlerAdjustedPoint
2456
     *            <code>point</code> adjusted
2457
     *
2458
     * @return distance from <code>point</code> to the adjusted one. If there is
2459
     *         no
2460
     *         adjustment, returns <code>Double.MAX_VALUE</code>.
2461
     */
2462

    
2463
    private double adjustToHandler(Point2D point,
2464
        Point2D mapHandlerAdjustedPoint) {
2465

    
2466
        if (!isRefentEnabled())
2467
            return Double.MAX_VALUE;
2468

    
2469
        List layersToSnap = getMapContext().getLayersToSnap();
2470

    
2471
        double mapTolerance =
2472
            vp.toMapDistance(mapControlManager.getTolerance());
2473
        double minDist = mapTolerance;
2474
        double middleTol = mapTolerance * 0.5;
2475
        Point2D mapPoint = point;
2476
        Envelope r = null;
2477
        try {
2478
            r =
2479
                geomManager.createEnvelope(mapPoint.getX() - middleTol,
2480
                    mapPoint.getY() - middleTol, mapPoint.getX() + middleTol,
2481
                    mapPoint.getY() + middleTol, SUBTYPES.GEOM2D);
2482
        } catch (Exception e1) {
2483
            LOG.info("Error creating the envelope", e1);
2484
            return Double.MAX_VALUE;
2485
        }
2486

    
2487
        usedSnap = null;
2488
        Point2D lastPoint = null;
2489
        if (previousPoint != null) {
2490
            lastPoint = new Point2D.Double(previousPoint[0], previousPoint[1]);
2491
        }
2492
        for (int j = 0; j < layersToSnap.size(); j++) {
2493
            FLyrVect lyrVect = (FLyrVect) layersToSnap.get(j);
2494
            SpatialCache cache = lyrVect.getSpatialCache();
2495
            if (lyrVect.isVisible()) {
2496
                // La lista de snappers est� siempre ordenada por prioridad. Los
2497
                // de mayor
2498
                // prioridad est�n primero.
2499
                List geoms = cache.query(r);
2500

    
2501
                for (int i = 0; i < mapControlManager.getSnapperCount(); i++)
2502
                {
2503
                    ISnapper theSnapper = mapControlManager.getSnapperAt(i);
2504
                    if (theSnapper instanceof ISnapperGeometriesVectorial)
2505
                    {
2506
                        ((ISnapperGeometriesVectorial)theSnapper).setGeometries(geoms);
2507
                    }
2508
                }
2509

    
2510
                for (int n = 0; n < geoms.size(); n++) {
2511
                    Geometry geom = (Geometry) geoms.get(n);
2512
                    for (int i = 0; i < mapControlManager.getSnapperCount(); i++) {
2513
                        ISnapper theSnapper = mapControlManager.getSnapperAt(i);
2514
                        if (!theSnapper.isEnabled())
2515
                            continue;
2516

    
2517
                        if (usedSnap != null) {
2518
                            // Si ya tenemos un snap y es de alta prioridad,
2519
                            // cogemos ese. (A no ser que en otra capa
2520
                            // encontremos un snapper mejor)
2521
                            if (theSnapper.getPriority() > usedSnap
2522
                                .getPriority())
2523
                                break;
2524
                        }
2525
                        // SnappingVisitor snapVisitor = null;
2526
                        Point2D theSnappedPoint = null;
2527
                        if (theSnapper instanceof ISnapperVectorial) {
2528
                            theSnappedPoint =
2529
                                ((ISnapperVectorial) theSnapper).getSnapPoint(
2530
                                    point, geom, mapTolerance, lastPoint);
2531
                        }
2532
                        if (theSnapper instanceof ISnapperRaster) {
2533
                            ISnapperRaster snapRaster =
2534
                                (ISnapperRaster) theSnapper;
2535
                            theSnappedPoint =
2536
                                snapRaster.getSnapPoint(this, point,
2537
                                    mapTolerance, lastPoint);
2538
                        }
2539

    
2540
                        if (theSnappedPoint != null) {
2541
                            double distAux = theSnappedPoint.distance(point);
2542
                            if (minDist > distAux) {
2543
                                minDist = distAux;
2544
                                usedSnap = theSnapper;
2545
                                mapHandlerAdjustedPoint
2546
                                    .setLocation(theSnappedPoint);
2547
                            }
2548
                        }
2549
                    }
2550
                } // for n
2551
            } // visible
2552
        }
2553
        if (usedSnap != null)
2554
            return minDist;
2555
        return Double.MAX_VALUE;
2556

    
2557
    }
2558

    
2559
    /**
2560
     * Determines if snap tools are enabled or disabled.
2561
     *
2562
     * @return <code>true</code> to enable the snap tools; <code>false</code> to
2563
     *         disable them
2564
     *
2565
     * @see #setRefentEnabled(boolean)
2566
     */
2567
    public boolean isRefentEnabled() {
2568
        return bRefent;
2569
    }
2570

    
2571
    /**
2572
     * <p>
2573
     * Tries to find the nearest geometry or grid control point by the position
2574
     * of the current snap tool.
2575
     * </p>
2576
     *
2577
     * <p>
2578
     * Prioritizes the grid control points than the geometries ones.
2579
     * </p>
2580
     *
2581
     * <p>
2582
     * If finds any near, stores the <i>map</i> and <i>pixel</i> coordinates for
2583
     * the snap, and enables the <code>bForceCoord</code> attribute for the next
2584
     * draw of the mouse's cursor.
2585
     * </p>
2586
     *
2587
     * @param point
2588
     *            current mouse 2D position
2589
     */
2590
    public void calculateSnapPoint(Point2D point) {
2591
        // Se comprueba el ajuste a rejilla
2592

    
2593
        Point2D gridAdjustedPoint = vp.toMapPoint(point);
2594
        double minDistance = Double.MAX_VALUE;
2595

    
2596
        minDistance = cadgrid.adjustToGrid(gridAdjustedPoint);
2597
        if (minDistance < Double.MAX_VALUE) {
2598
            adjustedPoint = vp.fromMapPoint(gridAdjustedPoint);
2599
            mapAdjustedPoint = gridAdjustedPoint;
2600
        } else {
2601
            mapAdjustedPoint = null;
2602
        }
2603

    
2604
        Point2D handlerAdjustedPoint = null;
2605

    
2606
        // Se comprueba el ajuste a los handlers
2607
        if (mapAdjustedPoint != null) {
2608
            handlerAdjustedPoint = (Point2D) mapAdjustedPoint.clone(); // getMapControl().getViewPort().toMapPoint(point);
2609
        } else {
2610
            handlerAdjustedPoint = vp.toMapPoint(point);
2611
        }
2612

    
2613
        Point2D mapPoint = new Point2D.Double();
2614
        double distance = adjustToHandler(handlerAdjustedPoint, mapPoint);
2615

    
2616
        if (distance < minDistance) {
2617
            bForceCoord = true;
2618
            adjustedPoint = vp.fromMapPoint(mapPoint);
2619
            mapAdjustedPoint = mapPoint;
2620
            minDistance = distance;
2621
        }
2622

    
2623
        // Si no hay ajuste
2624
        if (minDistance == Double.MAX_VALUE) {
2625
            adjustedPoint = point;
2626
            mapAdjustedPoint = null;
2627
        }
2628
    }
2629

    
2630
    /**
2631
     * Sets the snap tools enabled or disabled.
2632
     *
2633
     * @param activated
2634
     *            <code>true</code> to enable the snap tools; <code>false</code>
2635
     *            to disable them
2636
     *
2637
     * @see #isRefentEnabled()
2638
     */
2639
    public void setRefentEnabled(boolean activated) {
2640
        bRefent = activated;
2641
    }
2642

    
2643
    public Grid getGrid() {
2644
        return cadgrid;
2645
    }
2646

    
2647
    public void update(Observable observable, Object notification) {
2648
        DataStoreNotification ddsn = (DataStoreNotification) notification;
2649
        String type = ddsn.getType();
2650
        if (type.equals(FeatureStoreNotification.AFTER_UNDO)
2651
            || type.equals(FeatureStoreNotification.AFTER_REDO)) {
2652
            repaint();
2653
        }
2654
    }
2655

    
2656
    /**
2657
     * @return the adjustedPoint
2658
     */
2659
    public Point2D getAdjustedPoint() {
2660
        return adjustedPoint;
2661
    }
2662

    
2663
    /**
2664
     * @return the mapAdjustedPoint
2665
     */
2666
    public Point2D getMapAdjustedPoint() {
2667
        return mapAdjustedPoint;
2668
    }
2669
    
2670
    public Point2D convertToMapPoint(Point2D point, boolean useSnapping) {
2671
        Point2D p;
2672
        if (useSnapping) {
2673
            this.calculateSnapPoint(point);
2674
            p = this.getMapAdjustedPoint();
2675
            if (p==null) {
2676
               p = this.getViewPort().toMapPoint(point);
2677
            }
2678
         } else {
2679
             p = this.getViewPort().toMapPoint(point);
2680
         }
2681
       return p;
2682
     }
2683

    
2684
    /**
2685
     * Gets the selected point. If the snapping is enabled
2686
     * it returns the selected point.
2687
     *
2688
     * @return
2689
     *         The selected point
2690
     */
2691
    public Point2D getPoint() {
2692
        if (mapAdjustedPoint != null) {
2693
            return mapAdjustedPoint;
2694
        } else {
2695
            return getViewPort().toMapPoint(adjustedPoint);
2696
        }
2697
    }
2698

    
2699
        public synchronized void dispose() {
2700
                // Check if we have already been disposed, and don't do it again
2701
                if (!disposed && ToolsLocator.getDisposableManager().release(this)) {
2702
                        drawer.setShutdown(true);
2703
                        disposed = true;
2704
                }
2705
        }
2706

    
2707
    public boolean isDefaultMouseWheelEnabled() {
2708
        return defaultMouseWheelEnabled;
2709
    }
2710

    
2711
    public void setDefaultMouseWheelEnabled(boolean defaultMouseWheelEnabled) {
2712
        this.defaultMouseWheelEnabled = defaultMouseWheelEnabled;
2713
    }
2714

    
2715
    private ChangeListenerHelper changeToolListenerHelper;
2716
    
2717
    public void addChangeToolListener(ChangeListener listener) {
2718
        this.changeToolListenerHelper.addChangeListener(listener);
2719
    }
2720
    
2721
    public ChangeListener[] getChangeToolListeners() {
2722
        return this.changeToolListenerHelper.getChangeListeners();
2723
    }
2724

    
2725
    public void removeChangeToolListener(ChangeListener listener) {
2726
        this.changeToolListenerHelper.removeChangeListener(listener);
2727
    }
2728

    
2729
    public void removeAllChangeToolListener() {
2730
        this.changeToolListenerHelper.removeAllChangeListener();
2731
    }
2732

    
2733
    public boolean hasChangeToolListeners() {
2734
        return this.changeToolListenerHelper.hasChangeListeners();
2735
    }      
2736
}