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 @ 47421

History | View | Annotate | Download (90.3 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.ArrayList;
47
import java.util.Comparator;
48
import java.util.HashMap;
49
import java.util.List;
50
import java.util.Map;
51
import java.util.Set;
52
import java.util.TreeMap;
53
import java.util.prefs.Preferences;
54

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

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

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

    
267
    public class AddLayerEvent extends ActionEvent {
268

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

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

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

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

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

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

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

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

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

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

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

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

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

    
422
    // private boolean isCancelled = true;
423

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
583
    });
584

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

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

    
598
        private boolean disposed = false;
599

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

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

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

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

    
652
        vp = theMapContext.getViewPort();
653

    
654
        setMapContext(theMapContext);
655

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

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

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

    
741
        mapContext = model;
742

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

    
750
        mapContext.addAtomicEventListener(mapContextListener);
751

    
752
        status = DESACTUALIZADO;
753
    }
754

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1149
        try {
1150
            mapControlDrawer.startDrawing(this);
1151
        } catch (InterruptedException e) {
1152
            LOG.info("Error locking the MapControlDrawer", e);
1153
        }
1154
        mapControlDrawer.setGraphics(g);
1155
        mapControlDrawer.stopDrawing(this);
1156
        mapControlDrawer.setViewPort(getMapContext().getViewPort());
1157

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

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

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

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

    
1247
    /**
1248
     * @deprecated use {@link #drawMap(boolean)} instead, or even
1249
     * better {@link #getMapContext()}.invalidate().
1250
     */
1251
    public void drawGraphics() {
1252
        drawMap(false);
1253
    }
1254

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

    
1261
    /**
1262
     * @see java.awt.event.ComponentListener#componentMoved(java.awt.event.ComponentEvent)
1263
     */
1264
    public void componentMoved(ComponentEvent e) {
1265
    }
1266

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

    
1285
    /**
1286
     * @see java.awt.event.ComponentListener#componentShown(java.awt.event.ComponentEvent)
1287
     */
1288
    public void componentShown(ComponentEvent e) {
1289
    }
1290

    
1291
    /**
1292
     * @see ExceptionHandlingSupport#addExceptionListener(ExceptionListener)
1293
     */
1294
    public void addExceptionListener(ExceptionListener o) {
1295
        exceptionHandlingSupport.addExceptionListener(o);
1296
    }
1297

    
1298
    /**
1299
     * @see ExceptionHandlingSupport#removeExceptionListener(ExceptionListener)
1300
     */
1301
    public boolean removeExceptionListener(ExceptionListener o) {
1302
        return exceptionHandlingSupport.removeExceptionListener(o);
1303
    }
1304

    
1305
    /**
1306
     * @see ExceptionHandlingSupport#throwException(Throwable)
1307
     */
1308
    protected void throwException(Throwable t) {
1309
        exceptionHandlingSupport.throwException(t);
1310
    }
1311

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

    
1325
        /**
1326
         * <p>
1327
         * Creates a new <code>PaintingRequest
1328
         * </p>
1329
         * instance.</p>
1330
         */
1331
        public PaintingRequest() {
1332
        }
1333

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

    
1396
            } catch (Throwable e) {
1397
                timer.stop();
1398
                LOG.warn("Problems drawing mapcontext.",e);
1399
                throwException(e);
1400
            }
1401
        }
1402
    }
1403

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

    
1430
        // Una mini cola de 2. No acumulamos peticiones de dibujado
1431
        // dibujamos solo lo �ltimo que nos han pedido.
1432

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

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

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

    
1466
        /**
1467
         * <p>
1468
         * Notifies the <code>Drawer2</code>'s worker to finish or continue with
1469
         * its process.
1470
         * </p>
1471
         *
1472
         * @see #setShutdown(boolean)
1473
         */
1474
        private boolean shutdown;
1475

    
1476
                private Thread worker;
1477

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

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

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

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

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

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

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

    
1629
        /**
1630
         * <p>
1631
         * Determines if the drawing task must be canceled or not.
1632
         * </p>
1633
         *
1634
         * @see #isCanceled()
1635
         * @see #setCanceled(boolean)
1636
         */
1637
        private boolean cancel = false;
1638

    
1639
        /**
1640
         * Creates a new <code>CancelDraw</code> object.
1641
         */
1642
        public CancelDraw() {
1643
        }
1644

    
1645
        /*
1646
         * (non-Javadoc)
1647
         *
1648
         * @see com.iver.utiles.swing.threads.Cancellable#setCanceled(boolean)
1649
         */
1650
        public void setCanceled(boolean b) {
1651
            cancel = b;
1652
        }
1653

    
1654
        /*
1655
         * (non-Javadoc)
1656
         *
1657
         * @see com.iver.utiles.swing.threads.Cancellable#isCanceled()
1658
         */
1659
        public boolean isCanceled() {
1660
            return cancel;
1661
        }
1662
    }
1663

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

    
1693
        /**
1694
         * <p>
1695
         * Used to avoid mouse wheel move events closed.
1696
         * </p>
1697
         *
1698
         * <p>
1699
         * If a mouse wheel move event is produced
1700
         */
1701
        long t1;
1702

    
1703
        /**
1704
         * <p>
1705
         * Position of the mouse, in map coordinates.
1706
         * </p>
1707
         *
1708
         * <p>
1709
         * This point coordinates will be used as center of the <i>zoom</i>
1710
         * operation.
1711
         * </p>
1712
         */
1713
        Point2D pReal;
1714

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

    
1731
                if (mapAdjustedPoint != null) {
1732
                    p = mapAdjustedPoint;
1733
                } else {
1734
                    p = vp.toMapPoint(adjustedPoint);
1735
                }
1736
                if(p!=null){
1737
                    previousPoint = new double[] { p.getX(), p.getY() };
1738
                }
1739
            } catch (BehaviorException t) {
1740
                throwException(t);
1741
            }
1742
        }
1743

    
1744
        /**
1745
         * @see java.awt.event.MouseListener#mouseEntered(java.awt.event.MouseEvent)
1746
         * @see Behavior#mouseEntered(MouseEvent)
1747
         */
1748
        public void mouseEntered(MouseEvent e) {
1749
            setToolMouse();
1750
            try {
1751
                if (currentMapTool != null) {
1752
                    currentMapTool.mouseEntered(e);
1753
                }
1754
            } catch (BehaviorException t) {
1755
                throwException(t);
1756
            }
1757
        }
1758

    
1759
        /**
1760
         * @see java.awt.event.MouseListener#mouseExited(java.awt.event.MouseEvent)
1761
         * @see Behavior#mouseExited(MouseEvent)
1762
         */
1763
        public void mouseExited(MouseEvent e) {
1764
            try {
1765
                if (currentMapTool != null) {
1766
                    currentMapTool.mouseExited(e);
1767
                }
1768
            } catch (BehaviorException t) {
1769
                throwException(t);
1770
            }
1771
            // Remove the snapping image if exist
1772
            usedSnap = null;
1773
            repaint();
1774
        }
1775

    
1776
        /**
1777
         * @see java.awt.event.MouseListener#mousePressed(java.awt.event.MouseEvent)
1778
         * @see Behavior#mousePressed(MouseEvent)
1779
         */
1780
        public void mousePressed(MouseEvent e) {
1781
            try {
1782
                if (currentMapTool != null) {
1783
                    currentMapTool.mousePressed(e);
1784
                }
1785
            } catch (BehaviorException t) {
1786
                throwException(t);
1787
            }
1788
        }
1789

    
1790
        /**
1791
         * @see java.awt.event.MouseListener#mouseReleased(java.awt.event.MouseEvent)
1792
         * @see Behavior#mouseReleased(MouseEvent)
1793
         */
1794
        public void mouseReleased(MouseEvent e) {
1795
            try {
1796
                if (currentMapTool != null) {
1797
                    currentMapTool.mouseReleased(e);
1798
                }
1799
            } catch (BehaviorException t) {
1800
                throwException(t);
1801
            }
1802
        }
1803

    
1804
        /**
1805
         * @see java.awt.event.MouseWheelListener#mouseWheelMoved(java.awt.event.MouseWheelEvent)
1806
         * @see Behavior#mouseWheelMoved(MouseWheelEvent)
1807
         */
1808
        public void mouseWheelMoved(MouseWheelEvent e) {
1809
            try {
1810
                if (currentMapTool == null) {
1811
                    return;
1812
                }
1813

    
1814
                currentMapTool.mouseWheelMoved(e);
1815

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

    
1835
                    /*
1836
                     * Point2D pReal = new
1837
                     * Point2D.Double(vp.getAdjustedExtent().getCenterX(),
1838
                     * vp.getAdjustedExtent().getCenterY());
1839
                     */
1840
                    int amount = e.getWheelRotation();
1841
                    double nuevoX;
1842
                    double nuevoY;
1843
                    double factor;
1844

    
1845
                    if (amount < 0) // nos acercamos
1846
                    {
1847
                        factor = 0.9;
1848
                    } else // nos alejamos
1849
                    {
1850
                        factor = 1.2;
1851
                    }
1852
                    if (vp.getExtent() != null) {
1853
                        nuevoX =
1854
                            pReal.getX()
1855
                                - ((vp.getExtent().getWidth() * factor) / 2.0);
1856
                        nuevoY =
1857
                            pReal.getY()
1858
                                - ((vp.getExtent().getHeight() * factor) / 2.0);
1859
                        double x = nuevoX;
1860
                        double y = nuevoY;
1861
                        double width = vp.getExtent().getWidth() * factor;
1862
                        double height = vp.getExtent().getHeight() * factor;
1863

    
1864
                        try {
1865
                            vp.setEnvelope(geomManager.createEnvelope(x, y, x
1866
                                + width, y + height, SUBTYPES.GEOM2D));
1867
                        } catch (CreateEnvelopeException e1) {
1868
                            LOG.info("Error creating the envelope", e);
1869
                        }
1870
                    }
1871

    
1872
                }
1873
            } catch (BehaviorException t) {
1874
                throwException(t);
1875
            }
1876
        }
1877

    
1878
        /**
1879
         * @see java.awt.event.MouseMotionListener#mouseDragged(java.awt.event.MouseEvent)
1880
         * @see Behavior#mouseDragged(MouseEvent)
1881
         */
1882
        public void mouseDragged(MouseEvent e) {
1883
            calculateSnapPoint(e.getPoint());
1884
            try {
1885
                if (currentMapTool != null) {
1886
                    currentMapTool.mouseDragged(e);
1887
                }
1888
            } catch (BehaviorException t) {
1889
                throwException(t);
1890
            }
1891
            repaint();
1892
        }
1893

    
1894
        /**
1895
         * @see java.awt.event.MouseMotionListener#mouseMoved(java.awt.event.MouseEvent)
1896
         * @see Behavior#mouseMoved(MouseEvent)
1897
         */
1898
        public void mouseMoved(MouseEvent e) {
1899
            calculateSnapPoint(e.getPoint());
1900
            try {
1901
                if (currentMapTool != null) {
1902
                    currentMapTool.mouseMoved(e);
1903
                }
1904
            } catch (BehaviorException t) {
1905
                throwException(t);
1906
            }
1907
            repaint();
1908
        }
1909
    }
1910

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

    
1938
        /**
1939
         * @see org.gvsig.fmap.mapcontext.events.listeners.AtomicEventListener#atomicEvent(org.gvsig.fmap.mapcontext.events.AtomicEvent)
1940
         */
1941
        public void atomicEvent(final AtomicEvent e) {
1942
            if (!SwingUtilities.isEventDispatchThread()) {
1943
                SwingUtilities.invokeLater(new Runnable() {
1944

    
1945
                    public void run() {
1946
                        atomicEvent(e);
1947
                    }
1948
                });
1949
                return;
1950
            }
1951
            LayerEvent[] layerEvents = e.getLayerEvents();
1952

    
1953
            for (int i = 0; i < layerEvents.length; i++) {
1954
                if (layerEvents[i].getProperty().equals("visible")) {
1955
                    drawMap(false);
1956
                    return;
1957
                } else
1958
                    if (layerEvents[i].getEventType() == LayerEvent.EDITION_CHANGED) {
1959
                        drawMap(false);
1960
                        return;
1961
                    }
1962
            }
1963

    
1964
            if (e.getColorEvents().length > 0) {
1965
                drawMap(false);
1966
                return;
1967
            }
1968

    
1969
            if (e.getExtentEvents().length > 0) {
1970
                drawMap(false);
1971
                return;
1972
            }
1973

    
1974
            if (e.getProjectionEvents().length > 0) {
1975
                // redraw = true;
1976
            }
1977

    
1978
            LayerCollectionEvent[] aux = e.getLayerCollectionEvents();
1979
            if (aux.length > 0) {
1980
                for (int i = 0; i < aux.length; i++) {
1981
                    if (aux[i].getAffectedLayer().getFLayerStatus()
1982
                        .isDriverLoaded()) {
1983
                        drawMap(false);
1984
                        return;
1985
                    }
1986
                }
1987

    
1988
            }
1989

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

    
1995
            if (e.getSelectionEvents().length > 0) {
1996
                drawMap(false);
1997
                return;
1998
            }
1999
        }
2000
    }
2001

    
2002
    /**
2003
     * <p>
2004
     * Gets the <code>ViewPort</code> of this component's {@link MapContext
2005
     * MapContext} .
2006
     * </p>
2007
     *
2008
     * @see MapContext#getViewPort()
2009
     */
2010
    public ViewPort getViewPort() {
2011
        return vp;
2012
    }
2013

    
2014
    /**
2015
     * <p>
2016
     * Returns all registered <code>Behavior</code> that can define a way to
2017
     * work with this <code>MapControl</code>.
2018
     * </p>
2019
     *
2020
     * @return registered <code>Behavior</code> to this <code>MapControl</code>
2021
     *
2022
     * @see #addBehavior(String, Behavior)
2023
     * @see #addBehavior(String, Behavior[])
2024
     * @see #getMapToolsKeySet()
2025
     * @see #hasTool(String)
2026
     */
2027
    public HashMap getNamesMapTools() {
2028
        return (HashMap) namesMapTools;
2029
    }
2030

    
2031
    /*
2032
     * (non-Javadoc)
2033
     *
2034
     * @see
2035
     * com.iver.cit.gvsig.fmap.edition.commands.CommandListener#commandRepaint()
2036
     */
2037
    public void commandRepaint() {
2038
        drawMap(false);
2039
    }
2040

    
2041
    /*
2042
     * (non-Javadoc)
2043
     *
2044
     * @see
2045
     * com.iver.cit.gvsig.fmap.edition.commands.CommandListener#commandRefresh()
2046
     */
2047
    public void commandRefresh() {
2048
        // TODO Auto-generated method stub
2049
    }
2050

    
2051
    /**
2052
     * <p>
2053
     * Equivalent operation to <i>undo</i>.
2054
     * </p>
2055
     *
2056
     * <p>
2057
     * Exchanges the previous tool with the current one.
2058
     * </p>
2059
     *
2060
     * @see #addBehavior(String, Behavior)
2061
     * @see #addBehavior(String, Behavior[])
2062
     * @see #setTool(String)
2063
     */
2064
    public void setPrevTool() {
2065
        setTool(prevTool);
2066
    }
2067

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

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

    
2135
    /**
2136
     * <p>
2137
     * Returns the listener used to catch all mouse events produced in this
2138
     * <code>MapControl</code> instance and that redirects the calls to the
2139
     * current map tool.
2140
     * </p>
2141
     *
2142
     * @return the map tool listener used
2143
     */
2144
    public MapToolListener getMapToolListener() {
2145
        return mapToolListener;
2146
    }
2147

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

    
2169
    /**
2170
     * <p>
2171
     * Sets the delay to the timer that refreshes this <code>MapControl</code>
2172
     * instance.
2173
     * </p>
2174
     *
2175
     * <p>
2176
     * <code>Delay (in ms) = 1000 / getDrawFrameRate()</code>
2177
     * </p>
2178
     *
2179
     * @see #getDrawFrameRate()
2180
     * @see #setDrawFrameRate(int)
2181
     */
2182
    public void applyFrameRate() {
2183
        if (MapContext.getDrawFrameRate() > 0) {
2184
            timer.setDelay(1000 / MapContext.getDrawFrameRate());
2185
        }
2186
    }
2187

    
2188
    /**
2189
     * <p>
2190
     * Determines if its enabled the repaint that invokes the timer according to
2191
     * {@link #getDrawFrameRate() #getDrawFrameRate()}.
2192
     * </p>
2193
     *
2194
     * @return <code>true</code> if its enabled; otherwise <code>false</code>
2195
     */
2196
    public static boolean isDrawAnimationEnabled() {
2197
        return drawAnimationEnabled;
2198
    }
2199

    
2200
    /**
2201
     * <p>
2202
     * Sets if its enabled the repaint that invokes the timer according to
2203
     * {@link #getDrawFrameRate() #getDrawFrameRate()}.
2204
     * </p>
2205
     *
2206
     * @param drawAnimationEnabled
2207
     *            <code>true</code> to enable the mode; otherwise
2208
     *            <code>false</code>
2209
     */
2210
    public static void setDrawAnimationEnabled(boolean drawAnimationEnabled) {
2211
        MapControl.drawAnimationEnabled = drawAnimationEnabled;
2212
    }
2213

    
2214
    /**
2215
     * <p>
2216
     * Gets the shared object that determines if a drawing process must be
2217
     * cancelled or can continue.
2218
     * </p>
2219
     *
2220
     * @return the shared object that determines if a drawing process must be
2221
     *         cancelled or can continue
2222
     */
2223
    public CancelDraw getCanceldraw() {
2224
        return canceldraw;
2225
    }
2226

    
2227
    /**
2228
     * <p>
2229
     * Gets the tool used in combination with the current tool of this
2230
     * <code>MapControl</code>.
2231
     * </p>
2232
     *
2233
     * @return the tool used in combination with the <code>currentMapTool</code>
2234
     *         ; <code>null</code> if there is
2235
     *         no combined tool
2236
     */
2237
    public Behavior getCombinedTool() {
2238
        return combinedTool;
2239
    }
2240

    
2241
    /**
2242
     * <p>
2243
     * Sets a tool to be used in combination with the current tool of this
2244
     * <code>MapControl</code>.
2245
     * </p>
2246
     *
2247
     * @param combinedTool
2248
     *            a tool to be used in combination with the current tool of
2249
     *            <code>MapControl</code>
2250
     */
2251
    public void setCombinedTool(Behavior combinedTool) {
2252
        this.combinedTool = combinedTool;
2253

    
2254
        if (currentMapTool == null) {
2255
            return;
2256
        }
2257

    
2258
        if (currentMapTool instanceof CompoundBehavior) {
2259
            ((CompoundBehavior) currentMapTool).addMapBehavior(combinedTool,
2260
                true);
2261
        } else {
2262
            currentMapTool =
2263
                new CompoundBehavior(new Behavior[] { currentMapTool });
2264
            ((CompoundBehavior) currentMapTool).addMapBehavior(combinedTool,
2265
                true);
2266
        }
2267

    
2268
    }
2269

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

    
2296
                ((CompoundBehavior) combinedTool).addMapBehavior(tool, true);
2297
            } else {
2298
                if (combinedTool.equals(tool)) {
2299
                    return;
2300
                }
2301

    
2302
                combinedTool =
2303
                    new CompoundBehavior(new Behavior[] { combinedTool });
2304
                ((CompoundBehavior) combinedTool).addMapBehavior(tool, true);
2305
                combinedTool.setMapControl(this);
2306
            }
2307
        }
2308

    
2309
        if (currentMapTool == null) {
2310
            return;
2311
        }
2312

    
2313
        if (currentMapTool instanceof CompoundBehavior) {
2314
            ((CompoundBehavior) currentMapTool).addMapBehavior(tool, true);
2315
        } else {
2316
            currentMapTool =
2317
                new CompoundBehavior(new Behavior[] { currentMapTool });
2318
            ((CompoundBehavior) currentMapTool).addMapBehavior(tool, true);
2319
        }
2320
    }
2321

    
2322
    /**
2323
     * <p>
2324
     * Removes the tool <code>tool</code> used in combination with the current
2325
     * tool of this <code>MapControl</code>.
2326
     * </p>
2327
     */
2328
    public void removeCombinedTool(Behavior tool) {
2329
        if ((currentMapTool != null)
2330
            && (currentMapTool instanceof CompoundBehavior)) {
2331
            ((CompoundBehavior) currentMapTool).removeMapBehavior(tool);
2332
        }
2333

    
2334
        if (combinedTool == null) {
2335
            return;
2336
        }
2337

    
2338
        if (combinedTool instanceof CompoundBehavior) {
2339
            ((CompoundBehavior) combinedTool).removeMapBehavior(tool);
2340
        } else {
2341
            combinedTool = null;
2342
        }
2343
    }
2344

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

    
2370
        double dx = prefs.getDouble("grid.distancex", cadgrid.getGridSizeX());
2371
        double dy = prefs.getDouble("grid.distancey", cadgrid.getGridSizeY());
2372

    
2373
        setGridVisibility(showGrid);
2374
        setAdjustGrid(adjustGrid);
2375
        cadgrid.setGridSizeX(dx);
2376
        cadgrid.setGridSizeY(dy);
2377
    }
2378

    
2379
    public void setAdjustGrid(boolean adjustGrid) {
2380
        cadgrid.setAdjustGrid(adjustGrid);
2381
    }
2382

    
2383
    public void setGridVisibility(boolean showGrid) {
2384
        cadgrid.setShowGrid(showGrid);
2385
        cadgrid.setViewPort(getViewPort());
2386
        this.repaint();
2387
    }
2388

    
2389
    /**
2390
     * Uses like a mouse pointer the image that provides the
2391
     * selected tool.
2392
     *
2393
     */
2394

    
2395
    private Image lastImageCursor = null;
2396
    private Cursor lastCursor = null;
2397

    
2398
    private void setToolMouse() {
2399
        Image imageCursor = getCurrentMapTool().getImageCursor();
2400
        if (imageCursor != null && imageCursor == lastImageCursor && lastCursor != null && lastCursor != transparentCursor) {
2401
            setCursor(lastCursor);
2402
            return;
2403
        }
2404
        lastImageCursor = imageCursor;
2405
        Toolkit toolkit = Toolkit.getDefaultToolkit();
2406
        Cursor c = toolkit.createCustomCursor(imageCursor, new Point(16, 16), "img");
2407
        setCursor(c);
2408
        lastCursor = c;
2409
    }
2410

    
2411
    /**
2412
     * Makes the mouse pointer transparent.
2413
     */
2414
    private void setTransparentMouse() {
2415
        setCursor(transparentCursor);
2416
        lastCursor = transparentCursor;
2417
    }
2418

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

    
2441
        if (usedSnap != null) {
2442
            usedSnap.draw(mapControlDrawer, adjustedPoint);
2443
            setTransparentMouse();
2444
        } else {
2445
            setToolMouse();
2446
        }
2447
    }
2448

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

    
2470
    private double adjustToHandler(Point2D point,
2471
        Point2D mapHandlerAdjustedPoint) {
2472

    
2473
        if (!isRefentEnabled())
2474
            return Double.MAX_VALUE;
2475

    
2476
        Set<FLyrVect> layersToSnap = getMapContext().getLayersToSnap();
2477

    
2478
        double mapTolerance =
2479
            vp.toMapDistance(mapControlManager.getTolerance());
2480
        double minDist = mapTolerance;
2481
        double middleTol = mapTolerance * 0.5;
2482
        Point2D mapPoint = point;
2483
        Envelope r = null;
2484
        try {
2485
            r =
2486
                geomManager.createEnvelope(mapPoint.getX() - middleTol,
2487
                    mapPoint.getY() - middleTol, mapPoint.getX() + middleTol,
2488
                    mapPoint.getY() + middleTol, SUBTYPES.GEOM2D);
2489
        } catch (Exception e1) {
2490
            LOG.info("Error creating the envelope", e1);
2491
            return Double.MAX_VALUE;
2492
        }
2493

    
2494
        usedSnap = null;
2495
        Point2D lastPoint = null;
2496
        if (previousPoint != null) {
2497
            lastPoint = new Point2D.Double(previousPoint[0], previousPoint[1]);
2498
        }
2499
        List<Geometry> geomsToSnap = new ArrayList();
2500
        for (FLyrVect lyrVect : layersToSnap) {
2501
            SpatialCache cache = lyrVect.getSpatialCache();
2502
            if (lyrVect.isVisible()) {
2503
                List geoms = cache.query(r);
2504
                for (Object geom : geoms) {
2505
                    if(geom instanceof Geometry){
2506
                        geomsToSnap.add((Geometry)geom);
2507
                        if(geomsToSnap.size() > 1000){
2508
                            break;
2509
                        }
2510
                    }
2511
                }
2512
                if(geomsToSnap.size() > 1000){
2513
                    break;
2514
                }
2515
            }
2516
        }
2517

    
2518
        for (int i = 0; i < mapControlManager.getSnapperCount(); i++) {
2519
            ISnapper theSnapper = mapControlManager.getSnapperAt(i);
2520
            if (theSnapper instanceof ISnapperGeometriesVectorial) {
2521
                ((ISnapperGeometriesVectorial)theSnapper).setGeometries(geomsToSnap);
2522
            }
2523
        }
2524

    
2525
        for (int n = 0; n < geomsToSnap.size(); n++) {
2526
            Geometry geom = (Geometry) geomsToSnap.get(n);
2527
            // La lista de snappers esta siempre ordenada por prioridad. Los
2528
            // de mayor prioridad estan primero.
2529
            for (int i = 0; i < mapControlManager.getSnapperCount(); i++) {
2530
                ISnapper theSnapper = mapControlManager.getSnapperAt(i);
2531
                if (!theSnapper.isEnabled())
2532
                    continue;
2533

    
2534
                if (usedSnap != null) {
2535
                    // Si ya tenemos un snap y es de alta prioridad,
2536
                    // cogemos ese. (A no ser que en otra capa
2537
                    // encontremos un snapper mejor)
2538
                    if (theSnapper.getPriority() > usedSnap.getPriority()) {
2539
                        break;
2540
                    }
2541
                }
2542
                // SnappingVisitor snapVisitor = null;
2543
                Point2D theSnappedPoint = null;
2544
                if (theSnapper instanceof ISnapperVectorial) {
2545
                    theSnappedPoint =
2546
                        ((ISnapperVectorial) theSnapper).getSnapPoint(
2547
                            point, geom, mapTolerance, lastPoint);
2548
                }
2549
                if (theSnapper instanceof ISnapperRaster) {
2550
                    ISnapperRaster snapRaster =
2551
                        (ISnapperRaster) theSnapper;
2552
                    theSnappedPoint =
2553
                        snapRaster.getSnapPoint(this, point,
2554
                            mapTolerance, lastPoint);
2555
                }
2556

    
2557
                if (theSnappedPoint != null) {
2558
                    double distAux = theSnappedPoint.distance(point);
2559
                    if (minDist > distAux) {
2560
                        minDist = distAux;
2561
                        usedSnap = theSnapper;
2562
                        mapHandlerAdjustedPoint
2563
                            .setLocation(theSnappedPoint);
2564
                    }
2565
                }
2566
            }
2567
        }
2568
        
2569
        if (usedSnap != null) {
2570
            return minDist;
2571
        }
2572
        return Double.MAX_VALUE;
2573

    
2574
    }
2575

    
2576
    /**
2577
     * Determines if snap tools are enabled or disabled.
2578
     *
2579
     * @return <code>true</code> to enable the snap tools; <code>false</code> to
2580
     *         disable them
2581
     *
2582
     * @see #setRefentEnabled(boolean)
2583
     */
2584
    public boolean isRefentEnabled() {
2585
        return bRefent;
2586
    }
2587

    
2588
    /**
2589
     * <p>
2590
     * Tries to find the nearest geometry or grid control point by the position
2591
     * of the current snap tool.
2592
     * </p>
2593
     *
2594
     * <p>
2595
     * Prioritizes the grid control points than the geometries ones.
2596
     * </p>
2597
     *
2598
     * <p>
2599
     * If finds any near, stores the <i>map</i> and <i>pixel</i> coordinates for
2600
     * the snap, and enables the <code>bForceCoord</code> attribute for the next
2601
     * draw of the mouse's cursor.
2602
     * </p>
2603
     *
2604
     * @param point
2605
     *            current mouse 2D position
2606
     */
2607
    public void calculateSnapPoint(Point2D point) {
2608
        // Se comprueba el ajuste a rejilla
2609

    
2610
        Point2D gridAdjustedPoint = vp.toMapPoint(point);
2611
        double minDistance = Double.MAX_VALUE;
2612

    
2613
        minDistance = cadgrid.adjustToGrid(gridAdjustedPoint);
2614
        if (minDistance < Double.MAX_VALUE) {
2615
            adjustedPoint = vp.fromMapPoint(gridAdjustedPoint);
2616
            mapAdjustedPoint = gridAdjustedPoint;
2617
        } else {
2618
            mapAdjustedPoint = null;
2619
        }
2620

    
2621
        Point2D handlerAdjustedPoint = null;
2622

    
2623
        // Se comprueba el ajuste a los handlers
2624
        if (mapAdjustedPoint != null) {
2625
            handlerAdjustedPoint = (Point2D) mapAdjustedPoint.clone(); // getMapControl().getViewPort().toMapPoint(point);
2626
        } else {
2627
            handlerAdjustedPoint = vp.toMapPoint(point);
2628
        }
2629

    
2630
        Point2D mapPoint = new Point2D.Double();
2631
        double distance = adjustToHandler(handlerAdjustedPoint, mapPoint);
2632

    
2633
        if (distance < minDistance) {
2634
            bForceCoord = true;
2635
            adjustedPoint = vp.fromMapPoint(mapPoint);
2636
            mapAdjustedPoint = mapPoint;
2637
            minDistance = distance;
2638
        }
2639

    
2640
        // Si no hay ajuste
2641
        if (minDistance == Double.MAX_VALUE) {
2642
            adjustedPoint = point;
2643
            mapAdjustedPoint = null;
2644
        }
2645
    }
2646

    
2647
    /**
2648
     * Sets the snap tools enabled or disabled.
2649
     *
2650
     * @param activated
2651
     *            <code>true</code> to enable the snap tools; <code>false</code>
2652
     *            to disable them
2653
     *
2654
     * @see #isRefentEnabled()
2655
     */
2656
    public void setRefentEnabled(boolean activated) {
2657
        bRefent = activated;
2658
    }
2659

    
2660
    public Grid getGrid() {
2661
        return cadgrid;
2662
    }
2663

    
2664
    public void update(Observable observable, Object notification) {
2665
        DataStoreNotification ddsn = (DataStoreNotification) notification;
2666
        String type = ddsn.getType();
2667
        if (type.equals(FeatureStoreNotification.AFTER_UNDO)
2668
            || type.equals(FeatureStoreNotification.AFTER_REDO)) {
2669
            repaint();
2670
        }
2671
    }
2672

    
2673
    /**
2674
     * @return the adjustedPoint
2675
     */
2676
    public Point2D getAdjustedPoint() {
2677
        return adjustedPoint;
2678
    }
2679

    
2680
    /**
2681
     * @return the mapAdjustedPoint
2682
     */
2683
    public Point2D getMapAdjustedPoint() {
2684
        return mapAdjustedPoint;
2685
    }
2686
    
2687
    public Point2D convertToMapPoint(Point2D point, boolean useSnapping) {
2688
        Point2D p;
2689
        if (useSnapping) {
2690
            this.calculateSnapPoint(point);
2691
            p = this.getMapAdjustedPoint();
2692
            if (p==null) {
2693
               p = this.getViewPort().toMapPoint(point);
2694
            }
2695
         } else {
2696
             p = this.getViewPort().toMapPoint(point);
2697
         }
2698
       return p;
2699
     }
2700

    
2701
    /**
2702
     * Gets the selected point. If the snapping is enabled
2703
     * it returns the selected point.
2704
     *
2705
     * @return
2706
     *         The selected point
2707
     */
2708
    public Point2D getPoint() {
2709
        if (mapAdjustedPoint != null) {
2710
            return mapAdjustedPoint;
2711
        } else {
2712
            return getViewPort().toMapPoint(adjustedPoint);
2713
        }
2714
    }
2715

    
2716
        public synchronized void dispose() {
2717
                // Check if we have already been disposed, and don't do it again
2718
                if (!disposed && ToolsLocator.getDisposableManager().release(this)) {
2719
                        drawer.setShutdown(true);
2720
                        disposed = true;
2721
                }
2722
        }
2723

    
2724
    public boolean isDefaultMouseWheelEnabled() {
2725
        return defaultMouseWheelEnabled;
2726
    }
2727

    
2728
    public void setDefaultMouseWheelEnabled(boolean defaultMouseWheelEnabled) {
2729
        this.defaultMouseWheelEnabled = defaultMouseWheelEnabled;
2730
    }
2731

    
2732
    private ChangeListenerHelper changeToolListenerHelper;
2733
    
2734
    public void addChangeToolListener(ChangeListener listener) {
2735
        this.changeToolListenerHelper.addChangeListener(listener);
2736
    }
2737
    
2738
    public ChangeListener[] getChangeToolListeners() {
2739
        return this.changeToolListenerHelper.getChangeListeners();
2740
    }
2741

    
2742
    public void removeChangeToolListener(ChangeListener listener) {
2743
        this.changeToolListenerHelper.removeChangeListener(listener);
2744
    }
2745

    
2746
    public void removeAllChangeToolListener() {
2747
        this.changeToolListenerHelper.removeAllChangeListener();
2748
    }
2749

    
2750
    public boolean hasChangeToolListeners() {
2751
        return this.changeToolListenerHelper.hasChangeListeners();
2752
    }      
2753
}