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

History | View | Annotate | Download (88.2 KB)

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

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

    
54
import javax.swing.JComponent;
55
import javax.swing.SwingUtilities;
56
import javax.swing.Timer;
57

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

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

    
263
    public class AddLayerEvent extends ActionEvent {
264

    
265
        private static final long serialVersionUID = -1857839388303164091L;
266

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

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

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

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

    
310
    public static final int PAINT_PARTIAL_DRAWING_LAYERS = 0;
311
    public static final int PAINT_WHEN_DRAW_LAYERS_IS_COMPLETED = 1;
312

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

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

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

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

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

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

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

    
418
    // private boolean isCancelled = true;
419

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

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

    
456
    /**
457
     * <p>
458
     * Manager of all <code>MapControl</code> painting requests.
459
     * </p>
460
     */
461
    private Drawer drawer;
462

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

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

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

    
501
    /**
502
     * <p>
503
     * Name of the previous tool used.
504
     * </p>
505
     */
506
    protected String prevTool;
507

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

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

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

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

    
558
    /**
559
     * Stores the 2D map coordinates of the last point added.
560
     */
561
    private double[] previousPoint = null;
562

    
563
    private boolean defaultMouseWheelEnabled = true;
564
    
565
    protected static MapControlManager mapControlManager =
566
        MapControlLocator.getMapControlManager();
567

    
568
    private static TreeMap selected = new TreeMap(new Comparator() {
569

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

    
579
    });
580

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

    
588
    /**
589
     * Renderer used to draw the layers.
590
     */
591
    private MapControlDrawer mapControlDrawer = null;
592
        private Cursor transparentCursor;
593

    
594
        private boolean disposed = false;
595

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

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

    
640
        setDoubleBuffered(false);
641
        setOpaque(true);
642
        status = DESACTUALIZADO;
643

    
644
        // Clase usada para cancelar el dibujado
645
        canceldraw = new CancelDraw();
646

    
647
        vp = theMapContext.getViewPort();
648

    
649
        setMapContext(theMapContext);
650

    
651
        // eventos
652
        this.addComponentListener(this);
653
        this.addMouseListener(mapToolListener);
654
        this.addMouseMotionListener(mapToolListener);
655
        this.addMouseWheelListener(mapToolListener);
656

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

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

    
733
        mapContext = model;
734

    
735
        if (mapContext.getViewPort() == null) {
736
            mapContext.setViewPort(vp);
737
        } else {
738
            vp = mapContext.getViewPort();
739
            cadgrid.setViewPort(vp);
740
        }
741

    
742
        mapContext.addAtomicEventListener(mapContextListener);
743

    
744
        status = DESACTUALIZADO;
745
    }
746

    
747
    /**
748
     * @return the mapControlDrawer
749
     */
750
    public MapControlDrawer getMapControlDrawer() {
751
        return mapControlDrawer;
752
    }
753

    
754
    /**
755
     * @param mapControlDrawer
756
     *            the mapControlDrawer to set
757
     */
758
    public void setMapControlDrawer(MapControlDrawer mapControlDrawer) {
759
        this.mapControlDrawer = mapControlDrawer;
760
        this.mapControlDrawer.setViewPort(vp);
761
    }
762

    
763
    /**
764
     * <p>
765
     * Gets this component's {@link MapContext MapContext} projection.
766
     * </p>
767
     *
768
     * @return this component's {@link MapContext MapContext} projection
769
     *
770
     * @see MapContext#getProjection()
771
     * @see MapControl#setProjection(IProjection)
772
     */
773
    public IProjection getProjection() {
774
        return getMapContext().getProjection();
775
    }
776

    
777
    /**
778
     * <p>
779
     * Sets the projection to this component's {@link MapContext MapContext}.
780
     * </p>
781
     *
782
     * @param proj
783
     *            the kind of projection to this component's {@link MapContext
784
     *            MapContext}
785
     *
786
     * @see MapContext#setProjection(IProjection)
787
     * @see MapControl#getProjection()
788
     */
789
    public void setProjection(IProjection proj) {
790
        getMapContext().setProjection(proj);
791
    }
792

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

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

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

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

    
890
    /**
891
     * <p>
892
     * Returns a set view of the keys that identified the tools registered.
893
     * </p>
894
     *
895
     * @return a set view of the keys that identified the tools registered
896
     *
897
     * @see HashMap#keySet()
898
     *
899
     * @see #getNamesMapTools()
900
     * @see #addBehavior(String, Behavior)
901
     * @see #addBehavior(String, Behavior[])
902
     */
903
    public Set getMapToolsKeySet() {
904
        return namesMapTools.keySet();
905
    }
906

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

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

    
951
        if (combinedTool != null) {
952
            if (mapTool instanceof CompoundBehavior) {
953
                ((CompoundBehavior) mapTool).addMapBehavior(combinedTool, true);
954
            } else {
955
                currentMapTool =
956
                    new CompoundBehavior(new Behavior[] { currentMapTool });
957
                ((CompoundBehavior) currentMapTool).addMapBehavior(
958
                    combinedTool, true);
959
                currentMapTool.setMapControl(this);
960
            }
961
        }
962

    
963
        // this.setCursor(mapTool.getCursor());
964
    }
965

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

    
989
    /**
990
     * <p>
991
     * Returns the name of the current selected tool on this MapControl
992
     * </p>
993
     *
994
     * @return the name of the current's behavior tool associated to this
995
     *         component
996
     *
997
     * @see #getCurrentMapTool()
998
     * @see #setTool(String)
999
     */
1000
    public String getCurrentTool() {
1001
        return currentTool;
1002
    }
1003

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

    
1031
        /*
1032
         * while (!isCancelled) {
1033
         * if (!drawer.isAlive()) {
1034
         * // Si hemos llegado aqu� con un thread vivo, seguramente
1035
         * // no estamos actualizados.
1036
         *
1037
         * break;
1038
         * }
1039
         *
1040
         * }
1041
         * canceldraw.setCancel(false);
1042
         * isCancelled = false;
1043
         * drawerAlive = false;
1044
         */
1045
    }
1046

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

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

    
1137
        try {
1138
            mapControlDrawer.startDrawing(this);
1139
        } catch (InterruptedException e) {
1140
            LOG.info("Error locking the MapControlDrawer", e);
1141
        }
1142
        mapControlDrawer.setGraphics(g);
1143
        mapControlDrawer.stopDrawing(this);
1144
        mapControlDrawer.setViewPort(getMapContext().getViewPort());
1145

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

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

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

    
1220
    /**
1221
     * <p>
1222
     * Cancels any current drawing process, changing the status to
1223
     * <code>OUTDATED</code>, and forcing repaint only the layers dirty.
1224
     * </p>
1225
     *
1226
     * @see #cancelDrawing()
1227
     */
1228
    public void rePaintDirtyLayers() {
1229
        cancelDrawing();
1230
        status = DESACTUALIZADO;
1231
        repaint();
1232
    }
1233

    
1234
    /**
1235
     * @deprecated use {@link #drawMap(boolean)} instead, or even
1236
     * better {@link #getMapContext()}.invalidate().
1237
     */
1238
    public void drawGraphics() {
1239
        drawMap(false);
1240
    }
1241

    
1242
    /**
1243
     * @see java.awt.event.ComponentListener#componentHidden(java.awt.event.ComponentEvent)
1244
     */
1245
    public void componentHidden(ComponentEvent e) {
1246
    }
1247

    
1248
    /**
1249
     * @see java.awt.event.ComponentListener#componentMoved(java.awt.event.ComponentEvent)
1250
     */
1251
    public void componentMoved(ComponentEvent e) {
1252
    }
1253

    
1254
    /**
1255
     * @see java.awt.event.ComponentListener#componentResized(java.awt.event.ComponentEvent)
1256
     */
1257
    public void componentResized(ComponentEvent e) {
1258
        /*
1259
         * image = new BufferedImage(this.getWidth(), this.getHeight(),
1260
         * BufferedImage.TYPE_INT_ARGB);
1261
         * Graphics gTemp = image.createGraphics();
1262
         * gTemp.setColor(vp.getBackColor());
1263
         * gTemp.fillRect(0,0,getWidth(), getHeight());
1264
         * System.out.println("MapControl resized");
1265
         * // image = null;
1266
         * vp.setImageSize(new Dimension(getWidth(), getHeight()));
1267
         * getMapContext().getViewPort().setScale();
1268
         */
1269
        // drawMap(true);
1270
    }
1271

    
1272
    /**
1273
     * @see java.awt.event.ComponentListener#componentShown(java.awt.event.ComponentEvent)
1274
     */
1275
    public void componentShown(ComponentEvent e) {
1276
    }
1277

    
1278
    /**
1279
     * @see ExceptionHandlingSupport#addExceptionListener(ExceptionListener)
1280
     */
1281
    public void addExceptionListener(ExceptionListener o) {
1282
        exceptionHandlingSupport.addExceptionListener(o);
1283
    }
1284

    
1285
    /**
1286
     * @see ExceptionHandlingSupport#removeExceptionListener(ExceptionListener)
1287
     */
1288
    public boolean removeExceptionListener(ExceptionListener o) {
1289
        return exceptionHandlingSupport.removeExceptionListener(o);
1290
    }
1291

    
1292
    /**
1293
     * @see ExceptionHandlingSupport#throwException(Throwable)
1294
     */
1295
    protected void throwException(Throwable t) {
1296
        exceptionHandlingSupport.throwException(t);
1297
    }
1298

    
1299
    /**
1300
     * <p>
1301
     * Represents each <code>MapControl</code>'s data painting request.
1302
     * </p>
1303
     *
1304
     * <p>
1305
     * The request will be attended by a <code>Drawer2</code>, which will hold
1306
     * it since the <code>Drawer2</code>'s worker takes it, or arrives a new
1307
     * painting request, which will replace it.
1308
     * </p>
1309
     */
1310
    private class PaintingRequest {
1311

    
1312
        /**
1313
         * <p>
1314
         * Creates a new <code>PaintingRequest
1315
         * </p>
1316
         * instance.</p>
1317
         */
1318
        public PaintingRequest() {
1319
        }
1320

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

    
1383
            } catch (Throwable e) {
1384
                timer.stop();
1385
                LOG.warn("Problems drawing mapcontext.",e);
1386
                throwException(e);
1387
            }
1388
        }
1389
    }
1390

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

    
1417
        // Una mini cola de 2. No acumulamos peticiones de dibujado
1418
        // dibujamos solo lo �ltimo que nos han pedido.
1419

    
1420
        /**
1421
         * <p>
1422
         * Painting request that's been attended by the <code>Drawer2</code>'s
1423
         * worker.
1424
         * </p>
1425
         *
1426
         * @see #put(org.gvsig.fmap.mapcontrol.MapControl.PaintingRequest)
1427
         * @see #take()
1428
         */
1429
        private PaintingRequest paintingRequest;
1430

    
1431
        /**
1432
         * <p>
1433
         * Painting request waiting to be attended by the <code>Drawer2</code>'s
1434
         * worker.
1435
         * </p>
1436
         *
1437
         * @see #put(org.gvsig.fmap.mapcontrol.MapControl.PaintingRequest)
1438
         * @see #take()
1439
         */
1440
        private PaintingRequest waitingRequest;
1441

    
1442
        /**
1443
         * <p>
1444
         * Determines that the <code>Drawer2</code>'s worker is busy attending a
1445
         * painting request.
1446
         * </p>
1447
         *
1448
         * @see #put(org.gvsig.fmap.mapcontrol.MapControl.PaintingRequest)
1449
         * @see #take()
1450
         */
1451
        private boolean waiting;
1452

    
1453
        /**
1454
         * <p>
1455
         * Notifies the <code>Drawer2</code>'s worker to finish or continue with
1456
         * its process.
1457
         * </p>
1458
         *
1459
         * @see #setShutdown(boolean)
1460
         */
1461
        private boolean shutdown;
1462

    
1463
                private Thread worker;
1464

    
1465
        /**
1466
         * <p>
1467
         * Sets this <code>Drawer2</code>'s worker to finish or continue with
1468
         * its process.
1469
         * </p>
1470
         *
1471
         * @param isShutdown
1472
         *            a boolean value
1473
         */
1474
        public void setShutdown(boolean isShutdown) {
1475
            shutdown = isShutdown;
1476
            if (shutdown) {
1477
                    worker.interrupt();
1478
            }
1479
        }
1480

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

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

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

    
1565
        /**
1566
         * <p>
1567
         * Thread for attending painting requests.
1568
         * </p>
1569
         *
1570
         * <p>
1571
         * If there was no double buffer, sets the status to
1572
         * <code>OUTDATED</code> and finishes, otherwise takes the painting
1573
         * request (it's probably that would wait some time), cancel the
1574
         * previous drawing process, and starts processing the request.
1575
         * </p>
1576
         *
1577
         * @see Thread
1578
         */
1579
        private class Worker implements Runnable {
1580

    
1581
            /*
1582
             * (non-Javadoc)
1583
             *
1584
             * @see java.lang.Runnable#run()
1585
             */
1586
            public void run() {
1587
                while (!shutdown) {
1588
                    PaintingRequest p = take();
1589
                    // System.out.println("Pintando");
1590
                    if (image != null) {
1591
                        cancelDrawing();
1592
                        if (p != null) {
1593
                                p.paint();
1594
                        }
1595
                    } else {
1596
                        status = DESACTUALIZADO;
1597
                    }
1598
                }
1599
            }
1600
        }
1601
    }
1602

    
1603
    /**
1604
     * <p>
1605
     * An instance of <code>CancelDraw</code> will be shared by all this
1606
     * <code>MapControl</code>'s <code>MapContext</code> layers, allowing
1607
     * receive a notification that, when they're been drawn, to be cancelled.
1608
     * </p>
1609
     *
1610
     * @see Cancellable
1611
     *
1612
     * @author Fernando Gonz�lez Cort�s
1613
     */
1614
    public class CancelDraw implements Cancellable {
1615

    
1616
        /**
1617
         * <p>
1618
         * Determines if the drawing task must be canceled or not.
1619
         * </p>
1620
         *
1621
         * @see #isCanceled()
1622
         * @see #setCanceled(boolean)
1623
         */
1624
        private boolean cancel = false;
1625

    
1626
        /**
1627
         * Creates a new <code>CancelDraw</code> object.
1628
         */
1629
        public CancelDraw() {
1630
        }
1631

    
1632
        /*
1633
         * (non-Javadoc)
1634
         *
1635
         * @see com.iver.utiles.swing.threads.Cancellable#setCanceled(boolean)
1636
         */
1637
        public void setCanceled(boolean b) {
1638
            cancel = b;
1639
        }
1640

    
1641
        /*
1642
         * (non-Javadoc)
1643
         *
1644
         * @see com.iver.utiles.swing.threads.Cancellable#isCanceled()
1645
         */
1646
        public boolean isCanceled() {
1647
            return cancel;
1648
        }
1649
    }
1650

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

    
1680
        /**
1681
         * <p>
1682
         * Used to avoid mouse wheel move events closed.
1683
         * </p>
1684
         *
1685
         * <p>
1686
         * If a mouse wheel move event is produced
1687
         */
1688
        long t1;
1689

    
1690
        /**
1691
         * <p>
1692
         * Position of the mouse, in map coordinates.
1693
         * </p>
1694
         *
1695
         * <p>
1696
         * This point coordinates will be used as center of the <i>zoom</i>
1697
         * operation.
1698
         * </p>
1699
         */
1700
        Point2D pReal;
1701

    
1702
        /**
1703
         * @see java.awt.event.MouseListener#mouseClicked(java.awt.event.MouseEvent)
1704
         * @see Behavior#mouseClicked(MouseEvent)
1705
         */
1706
        public void mouseClicked(MouseEvent e) {
1707
            try {
1708
                if (currentMapTool != null) {
1709
                    currentMapTool.mouseClicked(e);
1710
                }
1711
                Point2D p;
1712

    
1713
                if (mapAdjustedPoint != null) {
1714
                    p = mapAdjustedPoint;
1715
                } else {
1716
                    p = vp.toMapPoint(adjustedPoint);
1717
                }
1718
                previousPoint = new double[] { p.getX(), p.getY() };
1719
            } catch (BehaviorException t) {
1720
                throwException(t);
1721
            }
1722
        }
1723

    
1724
        /**
1725
         * @see java.awt.event.MouseListener#mouseEntered(java.awt.event.MouseEvent)
1726
         * @see Behavior#mouseEntered(MouseEvent)
1727
         */
1728
        public void mouseEntered(MouseEvent e) {
1729
            setToolMouse();
1730
            try {
1731
                if (currentMapTool != null) {
1732
                    currentMapTool.mouseEntered(e);
1733
                }
1734
            } catch (BehaviorException t) {
1735
                throwException(t);
1736
            }
1737
        }
1738

    
1739
        /**
1740
         * @see java.awt.event.MouseListener#mouseExited(java.awt.event.MouseEvent)
1741
         * @see Behavior#mouseExited(MouseEvent)
1742
         */
1743
        public void mouseExited(MouseEvent e) {
1744
            try {
1745
                if (currentMapTool != null) {
1746
                    currentMapTool.mouseExited(e);
1747
                }
1748
            } catch (BehaviorException t) {
1749
                throwException(t);
1750
            }
1751
            // Remove the snapping image if exist
1752
            usedSnap = null;
1753
            repaint();
1754
        }
1755

    
1756
        /**
1757
         * @see java.awt.event.MouseListener#mousePressed(java.awt.event.MouseEvent)
1758
         * @see Behavior#mousePressed(MouseEvent)
1759
         */
1760
        public void mousePressed(MouseEvent e) {
1761
            try {
1762
                if (currentMapTool != null) {
1763
                    currentMapTool.mousePressed(e);
1764
                }
1765
            } catch (BehaviorException t) {
1766
                throwException(t);
1767
            }
1768
        }
1769

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

    
1784
        /**
1785
         * @see java.awt.event.MouseWheelListener#mouseWheelMoved(java.awt.event.MouseWheelEvent)
1786
         * @see Behavior#mouseWheelMoved(MouseWheelEvent)
1787
         */
1788
        public void mouseWheelMoved(MouseWheelEvent e) {
1789
            try {
1790
                if (currentMapTool == null) {
1791
                    return;
1792
                }
1793

    
1794
                currentMapTool.mouseWheelMoved(e);
1795

    
1796
                // Si el tool actual no ha consumido el evento
1797
                // entendemos que quiere el comportamiento por defecto.
1798
                if (!e.isConsumed() && defaultMouseWheelEnabled ) {
1799
                    // Para usar el primer punto sobre el que queremos centrar
1800
                    // el mapa, dejamos pasar un segundo para considerar el
1801
                    // siguiente
1802
                    // punto como v�lido.
1803
                    if (t1 == 0) {
1804
                        t1 = System.currentTimeMillis();
1805
                        pReal = vp.toMapPoint(e.getPoint());
1806
                    } else {
1807
                        long t2 = System.currentTimeMillis();
1808
                        if ((t2 - t1) > 1000) {
1809
                            t1 = 0;
1810
                        }
1811
                    }
1812
                    cancelDrawing();
1813
                    ViewPort vp = getViewPort();
1814

    
1815
                    /*
1816
                     * Point2D pReal = new
1817
                     * Point2D.Double(vp.getAdjustedExtent().getCenterX(),
1818
                     * vp.getAdjustedExtent().getCenterY());
1819
                     */
1820
                    int amount = e.getWheelRotation();
1821
                    double nuevoX;
1822
                    double nuevoY;
1823
                    double factor;
1824

    
1825
                    if (amount < 0) // nos acercamos
1826
                    {
1827
                        factor = 0.9;
1828
                    } else // nos alejamos
1829
                    {
1830
                        factor = 1.2;
1831
                    }
1832
                    if (vp.getExtent() != null) {
1833
                        nuevoX =
1834
                            pReal.getX()
1835
                                - ((vp.getExtent().getWidth() * factor) / 2.0);
1836
                        nuevoY =
1837
                            pReal.getY()
1838
                                - ((vp.getExtent().getHeight() * factor) / 2.0);
1839
                        double x = nuevoX;
1840
                        double y = nuevoY;
1841
                        double width = vp.getExtent().getWidth() * factor;
1842
                        double height = vp.getExtent().getHeight() * factor;
1843

    
1844
                        try {
1845
                            vp.setEnvelope(geomManager.createEnvelope(x, y, x
1846
                                + width, y + height, SUBTYPES.GEOM2D));
1847
                        } catch (CreateEnvelopeException e1) {
1848
                            LOG.info("Error creating the envelope", e);
1849
                        }
1850
                    }
1851

    
1852
                }
1853
            } catch (BehaviorException t) {
1854
                throwException(t);
1855
            }
1856
        }
1857

    
1858
        /**
1859
         * @see java.awt.event.MouseMotionListener#mouseDragged(java.awt.event.MouseEvent)
1860
         * @see Behavior#mouseDragged(MouseEvent)
1861
         */
1862
        public void mouseDragged(MouseEvent e) {
1863
            calculateSnapPoint(e.getPoint());
1864
            try {
1865
                if (currentMapTool != null) {
1866
                    currentMapTool.mouseDragged(e);
1867
                }
1868
            } catch (BehaviorException t) {
1869
                throwException(t);
1870
            }
1871
            repaint();
1872
        }
1873

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

    
1891
    /**
1892
     * <p<code>MapContextListener</code> listens all events produced in a
1893
     * <code>MapControl</code>'s <code>MapContext</code> object during an atomic
1894
     * period of time, and sets it to dirty, <i>executing
1895
     * <code>drawMap(false)</code>, if any of the
1896
     * following conditions is accomplished</i>:
1897
     * <ul>
1898
     * <li>Any of the <code>LayerEvent</code> in the <code>AtomicEvent</code>
1899
     * parameter notifies a <i>visibility change</i>.</li>
1900
     * <li>There is at least one <code>ColorEvent</code> in the
1901
     * <code>AtomicEvent</code> parameter.</li>
1902
     * <li>There is at least one <code>ExtentEvent</code> in the
1903
     * <code>AtomicEvent</code> parameter.</li>
1904
     * <li>Any of the <code>LayerCollectionEvent</code> in the
1905
     * <code>AtomicEvent</code> parameter notifies that a driver's layer has
1906
     * reloaded it successfully.</li>
1907
     * <li>There is at least one <code>LegendEvent</code> in the
1908
     * <code>AtomicEvent</code> parameter.</li>
1909
     * <li>There is at least one <code>SelectionEvent</code> in the
1910
     * <code>AtomicEvent</code> parameter.</li>
1911
     * </ul>
1912
     * </p>
1913
     *
1914
     * @author Fernando Gonz�lez Cort�s
1915
     */
1916
    public class MapContextListener implements AtomicEventListener {
1917

    
1918
        /**
1919
         * @see org.gvsig.fmap.mapcontext.events.listeners.AtomicEventListener#atomicEvent(org.gvsig.fmap.mapcontext.events.AtomicEvent)
1920
         */
1921
        public void atomicEvent(final AtomicEvent e) {
1922
            if (!SwingUtilities.isEventDispatchThread()) {
1923
                SwingUtilities.invokeLater(new Runnable() {
1924

    
1925
                    public void run() {
1926
                        atomicEvent(e);
1927
                    }
1928
                });
1929
                return;
1930
            }
1931
            LayerEvent[] layerEvents = e.getLayerEvents();
1932

    
1933
            for (int i = 0; i < layerEvents.length; i++) {
1934
                if (layerEvents[i].getProperty().equals("visible")) {
1935
                    drawMap(false);
1936
                    return;
1937
                } else
1938
                    if (layerEvents[i].getEventType() == LayerEvent.EDITION_CHANGED) {
1939
                        drawMap(false);
1940
                        return;
1941
                    }
1942
            }
1943

    
1944
            if (e.getColorEvents().length > 0) {
1945
                drawMap(false);
1946
                return;
1947
            }
1948

    
1949
            if (e.getExtentEvents().length > 0) {
1950
                drawMap(false);
1951
                return;
1952
            }
1953

    
1954
            if (e.getProjectionEvents().length > 0) {
1955
                // redraw = true;
1956
            }
1957

    
1958
            LayerCollectionEvent[] aux = e.getLayerCollectionEvents();
1959
            if (aux.length > 0) {
1960
                for (int i = 0; i < aux.length; i++) {
1961
                    if (aux[i].getAffectedLayer().getFLayerStatus()
1962
                        .isDriverLoaded()) {
1963
                        drawMap(false);
1964
                        return;
1965
                    }
1966
                }
1967

    
1968
            }
1969

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

    
1975
            if (e.getSelectionEvents().length > 0) {
1976
                drawMap(false);
1977
                return;
1978
            }
1979
        }
1980
    }
1981

    
1982
    /**
1983
     * <p>
1984
     * Gets the <code>ViewPort</code> of this component's {@link MapContext
1985
     * MapContext} .
1986
     * </p>
1987
     *
1988
     * @see MapContext#getViewPort()
1989
     */
1990
    public ViewPort getViewPort() {
1991
        return vp;
1992
    }
1993

    
1994
    /**
1995
     * <p>
1996
     * Returns all registered <code>Behavior</code> that can define a way to
1997
     * work with this <code>MapControl</code>.
1998
     * </p>
1999
     *
2000
     * @return registered <code>Behavior</code> to this <code>MapControl</code>
2001
     *
2002
     * @see #addBehavior(String, Behavior)
2003
     * @see #addBehavior(String, Behavior[])
2004
     * @see #getMapToolsKeySet()
2005
     * @see #hasTool(String)
2006
     */
2007
    public HashMap getNamesMapTools() {
2008
        return (HashMap) namesMapTools;
2009
    }
2010

    
2011
    /*
2012
     * (non-Javadoc)
2013
     *
2014
     * @see
2015
     * com.iver.cit.gvsig.fmap.edition.commands.CommandListener#commandRepaint()
2016
     */
2017
    public void commandRepaint() {
2018
        drawMap(false);
2019
    }
2020

    
2021
    /*
2022
     * (non-Javadoc)
2023
     *
2024
     * @see
2025
     * com.iver.cit.gvsig.fmap.edition.commands.CommandListener#commandRefresh()
2026
     */
2027
    public void commandRefresh() {
2028
        // TODO Auto-generated method stub
2029
    }
2030

    
2031
    /**
2032
     * <p>
2033
     * Equivalent operation to <i>undo</i>.
2034
     * </p>
2035
     *
2036
     * <p>
2037
     * Exchanges the previous tool with the current one.
2038
     * </p>
2039
     *
2040
     * @see #addBehavior(String, Behavior)
2041
     * @see #addBehavior(String, Behavior[])
2042
     * @see #setTool(String)
2043
     */
2044
    public void setPrevTool() {
2045
        setTool(prevTool);
2046
    }
2047

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

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

    
2115
    /**
2116
     * <p>
2117
     * Returns the listener used to catch all mouse events produced in this
2118
     * <code>MapControl</code> instance and that redirects the calls to the
2119
     * current map tool.
2120
     * </p>
2121
     *
2122
     * @return the map tool listener used
2123
     */
2124
    public MapToolListener getMapToolListener() {
2125
        return mapToolListener;
2126
    }
2127

    
2128
    // mapTool can be null, for instance, in 3D's navigation tools
2129
    /**
2130
     * <p>
2131
     * Sets <code>mapTool</code> as this <code>MapControl</code>'s current map
2132
     * tool.
2133
     *
2134
     * @param mapTool
2135
     *            a map tool, or <code>null</code> to disable the interaction
2136
     *            with the user
2137
     *
2138
     * @see #getCurrentMapTool()
2139
     * @see #getCurrentTool()
2140
     * @see #setTool(String)
2141
     * @see #setPrevTool()
2142
     * @see #addBehavior(String, Behavior)
2143
     * @see #addBehavior(String, Behavior[])
2144
     */
2145
    public void setCurrentMapTool(Behavior mapTool) {
2146
        currentMapTool = mapTool;
2147
    }
2148

    
2149
    /**
2150
     * <p>
2151
     * Sets the delay to the timer that refreshes this <code>MapControl</code>
2152
     * instance.
2153
     * </p>
2154
     *
2155
     * <p>
2156
     * <code>Delay (in ms) = 1000 / getDrawFrameRate()</code>
2157
     * </p>
2158
     *
2159
     * @see #getDrawFrameRate()
2160
     * @see #setDrawFrameRate(int)
2161
     */
2162
    public void applyFrameRate() {
2163
        if (MapContext.getDrawFrameRate() > 0) {
2164
            timer.setDelay(1000 / MapContext.getDrawFrameRate());
2165
        }
2166
    }
2167

    
2168
    /**
2169
     * <p>
2170
     * Determines if its enabled the repaint that invokes the timer according to
2171
     * {@link #getDrawFrameRate() #getDrawFrameRate()}.
2172
     * </p>
2173
     *
2174
     * @return <code>true</code> if its enabled; otherwise <code>false</code>
2175
     */
2176
    public static boolean isDrawAnimationEnabled() {
2177
        return drawAnimationEnabled;
2178
    }
2179

    
2180
    /**
2181
     * <p>
2182
     * Sets if its enabled the repaint that invokes the timer according to
2183
     * {@link #getDrawFrameRate() #getDrawFrameRate()}.
2184
     * </p>
2185
     *
2186
     * @param drawAnimationEnabled
2187
     *            <code>true</code> to enable the mode; otherwise
2188
     *            <code>false</code>
2189
     */
2190
    public static void setDrawAnimationEnabled(boolean drawAnimationEnabled) {
2191
        MapControl.drawAnimationEnabled = drawAnimationEnabled;
2192
    }
2193

    
2194
    /**
2195
     * <p>
2196
     * Gets the shared object that determines if a drawing process must be
2197
     * cancelled or can continue.
2198
     * </p>
2199
     *
2200
     * @return the shared object that determines if a drawing process must be
2201
     *         cancelled or can continue
2202
     */
2203
    public CancelDraw getCanceldraw() {
2204
        return canceldraw;
2205
    }
2206

    
2207
    /**
2208
     * <p>
2209
     * Gets the tool used in combination with the current tool of this
2210
     * <code>MapControl</code>.
2211
     * </p>
2212
     *
2213
     * @return the tool used in combination with the <code>currentMapTool</code>
2214
     *         ; <code>null</code> if there is
2215
     *         no combined tool
2216
     */
2217
    public Behavior getCombinedTool() {
2218
        return combinedTool;
2219
    }
2220

    
2221
    /**
2222
     * <p>
2223
     * Sets a tool to be used in combination with the current tool of this
2224
     * <code>MapControl</code>.
2225
     * </p>
2226
     *
2227
     * @param combinedTool
2228
     *            a tool to be used in combination with the current tool of
2229
     *            <code>MapControl</code>
2230
     */
2231
    public void setCombinedTool(Behavior combinedTool) {
2232
        this.combinedTool = combinedTool;
2233

    
2234
        if (currentMapTool == null) {
2235
            return;
2236
        }
2237

    
2238
        if (currentMapTool instanceof CompoundBehavior) {
2239
            ((CompoundBehavior) currentMapTool).addMapBehavior(combinedTool,
2240
                true);
2241
        } else {
2242
            currentMapTool =
2243
                new CompoundBehavior(new Behavior[] { currentMapTool });
2244
            ((CompoundBehavior) currentMapTool).addMapBehavior(combinedTool,
2245
                true);
2246
        }
2247

    
2248
    }
2249

    
2250
    /**
2251
     * <p>
2252
     * Adds a new tool as combined tool.
2253
     * </p>
2254
     * <p>
2255
     * The new tool will be stored with the previous combined tools, and will be
2256
     * combined with the current tool.
2257
     * </p>
2258
     * <p>
2259
     * If <code>tool</code> was already stored as a combined tool, doesn't adds
2260
     * it.
2261
     * </p>
2262
     *
2263
     * @param tool
2264
     *            a new tool to be used combined with the current tool
2265
     */
2266
    public void addCombinedBehavior(Behavior tool) {
2267
        tool.setMapControl(this);
2268
        if (combinedTool == null) {
2269
            combinedTool = tool;
2270
        } else {
2271
            if (combinedTool instanceof CompoundBehavior) {
2272
                if (((CompoundBehavior) combinedTool).containsBehavior(tool)) {
2273
                    return;
2274
                }
2275

    
2276
                ((CompoundBehavior) combinedTool).addMapBehavior(tool, true);
2277
            } else {
2278
                if (combinedTool.equals(tool)) {
2279
                    return;
2280
                }
2281

    
2282
                combinedTool =
2283
                    new CompoundBehavior(new Behavior[] { combinedTool });
2284
                ((CompoundBehavior) combinedTool).addMapBehavior(tool, true);
2285
                combinedTool.setMapControl(this);
2286
            }
2287
        }
2288

    
2289
        if (currentMapTool == null) {
2290
            return;
2291
        }
2292

    
2293
        if (currentMapTool instanceof CompoundBehavior) {
2294
            ((CompoundBehavior) currentMapTool).addMapBehavior(tool, true);
2295
        } else {
2296
            currentMapTool =
2297
                new CompoundBehavior(new Behavior[] { currentMapTool });
2298
            ((CompoundBehavior) currentMapTool).addMapBehavior(tool, true);
2299
        }
2300
    }
2301

    
2302
    /**
2303
     * <p>
2304
     * Removes the tool <code>tool</code> used in combination with the current
2305
     * tool of this <code>MapControl</code>.
2306
     * </p>
2307
     */
2308
    public void removeCombinedTool(Behavior tool) {
2309
        if ((currentMapTool != null)
2310
            && (currentMapTool instanceof CompoundBehavior)) {
2311
            ((CompoundBehavior) currentMapTool).removeMapBehavior(tool);
2312
        }
2313

    
2314
        if (combinedTool == null) {
2315
            return;
2316
        }
2317

    
2318
        if (combinedTool instanceof CompoundBehavior) {
2319
            ((CompoundBehavior) combinedTool).removeMapBehavior(tool);
2320
        } else {
2321
            combinedTool = null;
2322
        }
2323
    }
2324

    
2325
    /**
2326
     * <p>
2327
     * Updates the grid on the <code>ViewPort</code> of the associated
2328
     * <code>MapControl</code> object according the values in the
2329
     * {@link com.iver.cit.gvsig.gui.cad.CADToolAdapter.prefs.Preferences
2330
     * com.iver.cit.gvsig.gui.cad.CADToolAdapter.prefs.Preferences}.
2331
     * </p>
2332
     *
2333
     * <p>
2334
     * The preferences are:
2335
     * <ul>
2336
     * <li>Show/hide the grid.</li>
2337
     * <li>Adjust or not the grid.</li>
2338
     * <li>Horizontal ( X ) line separation.</li>
2339
     * <li>Vertical ( Y ) line separation.</li>
2340
     * </ul>
2341
     * </p>
2342
     */
2343
    public void initializeGrid() {
2344
        Preferences prefs = mapControlManager.getEditionPreferences();
2345
        boolean showGrid =
2346
            prefs.getBoolean("grid.showgrid", cadgrid.isShowGrid());
2347
        boolean adjustGrid =
2348
            prefs.getBoolean("grid.adjustgrid", cadgrid.isAdjustGrid());
2349

    
2350
        double dx = prefs.getDouble("grid.distancex", cadgrid.getGridSizeX());
2351
        double dy = prefs.getDouble("grid.distancey", cadgrid.getGridSizeY());
2352

    
2353
        setGridVisibility(showGrid);
2354
        setAdjustGrid(adjustGrid);
2355
        cadgrid.setGridSizeX(dx);
2356
        cadgrid.setGridSizeY(dy);
2357
    }
2358

    
2359
    public void setAdjustGrid(boolean adjustGrid) {
2360
        cadgrid.setAdjustGrid(adjustGrid);
2361
    }
2362

    
2363
    public void setGridVisibility(boolean showGrid) {
2364
        cadgrid.setShowGrid(showGrid);
2365
        cadgrid.setViewPort(getViewPort());
2366
        this.repaint();
2367
    }
2368

    
2369
    /**
2370
     * Uses like a mouse pointer the image that provides the
2371
     * selected tool.
2372
     *
2373
     */
2374

    
2375
    private Image lastImageCursor = null;
2376
    private Cursor lastCursor = null;
2377

    
2378
    private void setToolMouse() {
2379
        Image imageCursor = getCurrentMapTool().getImageCursor();
2380
        if (imageCursor != null && imageCursor == lastImageCursor && lastCursor != null && lastCursor != transparentCursor) {
2381
            setCursor(lastCursor);
2382
            return;
2383
        }
2384
        lastImageCursor = imageCursor;
2385
        Toolkit toolkit = Toolkit.getDefaultToolkit();
2386
        Cursor c = toolkit.createCustomCursor(imageCursor, new Point(16, 16), "img");
2387
        setCursor(c);
2388
        lastCursor = c;
2389
    }
2390

    
2391
    /**
2392
     * Makes the mouse pointer transparent.
2393
     */
2394
    private void setTransparentMouse() {
2395
        setCursor(transparentCursor);
2396
        lastCursor = transparentCursor;
2397
    }
2398

    
2399
    /**
2400
     * <p>
2401
     * Draws a 31x31 pixels cross round the mouse's cursor with an small
2402
     * geometry centered:
2403
     * <ul>
2404
     * <li><i>an square centered</i>: if isn't over a <i>control point</i>.
2405
     * <li><i>an small geometry centered according to the kind of control
2406
     * point</i>: if it's over a control point. In this case, the small geometry
2407
     * is drawn by a {@link ISnapper ISnapper} type object.<br>
2408
     * On the other hand, a light-yellowed background tool tip text with the
2409
     * type of <i>control point</i> will be displayed.</li>
2410
     * </p>
2411
     *
2412
     * @param g
2413
     *            <code>MapControl</code>'s graphics where the data will be
2414
     *            drawn
2415
     */
2416
    private void drawCursor() {
2417
        if (adjustedPoint == null) {
2418
            return;
2419
        }
2420

    
2421
        if (usedSnap != null) {
2422
            usedSnap.draw(mapControlDrawer, adjustedPoint);
2423
            setTransparentMouse();
2424
        } else {
2425
            setToolMouse();
2426
        }
2427
    }
2428

    
2429
    /**
2430
     * <p>
2431
     * Adjusts the <code>point</code> to the grid if its enabled, and sets
2432
     * <code>mapHandlerAdjustedPoint</code> with that new value.
2433
     * </p>
2434
     *
2435
     * <p>
2436
     * The value returned is the distance between those points: the original and
2437
     * the adjusted one.
2438
     * </p>
2439
     *
2440
     * @param point
2441
     *            point to adjust
2442
     * @param mapHandlerAdjustedPoint
2443
     *            <code>point</code> adjusted
2444
     *
2445
     * @return distance from <code>point</code> to the adjusted one. If there is
2446
     *         no
2447
     *         adjustment, returns <code>Double.MAX_VALUE</code>.
2448
     */
2449

    
2450
    private double adjustToHandler(Point2D point,
2451
        Point2D mapHandlerAdjustedPoint) {
2452

    
2453
        if (!isRefentEnabled())
2454
            return Double.MAX_VALUE;
2455

    
2456
        List layersToSnap = getMapContext().getLayersToSnap();
2457

    
2458
        double mapTolerance =
2459
            vp.toMapDistance(mapControlManager.getTolerance());
2460
        double minDist = mapTolerance;
2461
        double middleTol = mapTolerance * 0.5;
2462
        Point2D mapPoint = point;
2463
        Envelope r = null;
2464
        try {
2465
            r =
2466
                geomManager.createEnvelope(mapPoint.getX() - middleTol,
2467
                    mapPoint.getY() - middleTol, mapPoint.getX() + middleTol,
2468
                    mapPoint.getY() + middleTol, SUBTYPES.GEOM2D);
2469
        } catch (Exception e1) {
2470
            LOG.info("Error creating the envelope", e1);
2471
            return Double.MAX_VALUE;
2472
        }
2473

    
2474
        usedSnap = null;
2475
        Point2D lastPoint = null;
2476
        if (previousPoint != null) {
2477
            lastPoint = new Point2D.Double(previousPoint[0], previousPoint[1]);
2478
        }
2479
        for (int j = 0; j < layersToSnap.size(); j++) {
2480
            FLyrVect lyrVect = (FLyrVect) layersToSnap.get(j);
2481
            SpatialCache cache = lyrVect.getSpatialCache();
2482
            if (lyrVect.isVisible()) {
2483
                // La lista de snappers est� siempre ordenada por prioridad. Los
2484
                // de mayor
2485
                // prioridad est�n primero.
2486
                List geoms = cache.query(r);
2487

    
2488
                for (int i = 0; i < mapControlManager.getSnapperCount(); i++)
2489
                {
2490
                    ISnapper theSnapper = mapControlManager.getSnapperAt(i);
2491
                    if (theSnapper instanceof ISnapperGeometriesVectorial)
2492
                    {
2493
                        ((ISnapperGeometriesVectorial)theSnapper).setGeometries(geoms);
2494
                    }
2495
                }
2496

    
2497
                for (int n = 0; n < geoms.size(); n++) {
2498
                    Geometry geom = (Geometry) geoms.get(n);
2499
                    for (int i = 0; i < mapControlManager.getSnapperCount(); i++) {
2500
                        ISnapper theSnapper = mapControlManager.getSnapperAt(i);
2501
                        if (!theSnapper.isEnabled())
2502
                            continue;
2503

    
2504
                        if (usedSnap != null) {
2505
                            // Si ya tenemos un snap y es de alta prioridad,
2506
                            // cogemos ese. (A no ser que en otra capa
2507
                            // encontremos un snapper mejor)
2508
                            if (theSnapper.getPriority() > usedSnap
2509
                                .getPriority())
2510
                                break;
2511
                        }
2512
                        // SnappingVisitor snapVisitor = null;
2513
                        Point2D theSnappedPoint = null;
2514
                        if (theSnapper instanceof ISnapperVectorial) {
2515
                            theSnappedPoint =
2516
                                ((ISnapperVectorial) theSnapper).getSnapPoint(
2517
                                    point, geom, mapTolerance, lastPoint);
2518
                        }
2519
                        if (theSnapper instanceof ISnapperRaster) {
2520
                            ISnapperRaster snapRaster =
2521
                                (ISnapperRaster) theSnapper;
2522
                            theSnappedPoint =
2523
                                snapRaster.getSnapPoint(this, point,
2524
                                    mapTolerance, lastPoint);
2525
                        }
2526

    
2527
                        if (theSnappedPoint != null) {
2528
                            double distAux = theSnappedPoint.distance(point);
2529
                            if (minDist > distAux) {
2530
                                minDist = distAux;
2531
                                usedSnap = theSnapper;
2532
                                mapHandlerAdjustedPoint
2533
                                    .setLocation(theSnappedPoint);
2534
                            }
2535
                        }
2536
                    }
2537
                } // for n
2538
            } // visible
2539
        }
2540
        if (usedSnap != null)
2541
            return minDist;
2542
        return Double.MAX_VALUE;
2543

    
2544
    }
2545

    
2546
    /**
2547
     * Determines if snap tools are enabled or disabled.
2548
     *
2549
     * @return <code>true</code> to enable the snap tools; <code>false</code> to
2550
     *         disable them
2551
     *
2552
     * @see #setRefentEnabled(boolean)
2553
     */
2554
    public boolean isRefentEnabled() {
2555
        return bRefent;
2556
    }
2557

    
2558
    /**
2559
     * <p>
2560
     * Tries to find the nearest geometry or grid control point by the position
2561
     * of the current snap tool.
2562
     * </p>
2563
     *
2564
     * <p>
2565
     * Prioritizes the grid control points than the geometries ones.
2566
     * </p>
2567
     *
2568
     * <p>
2569
     * If finds any near, stores the <i>map</i> and <i>pixel</i> coordinates for
2570
     * the snap, and enables the <code>bForceCoord</code> attribute for the next
2571
     * draw of the mouse's cursor.
2572
     * </p>
2573
     *
2574
     * @param point
2575
     *            current mouse 2D position
2576
     */
2577
    public void calculateSnapPoint(Point point) {
2578
        // Se comprueba el ajuste a rejilla
2579

    
2580
        Point2D gridAdjustedPoint = vp.toMapPoint(point);
2581
        double minDistance = Double.MAX_VALUE;
2582

    
2583
        minDistance = cadgrid.adjustToGrid(gridAdjustedPoint);
2584
        if (minDistance < Double.MAX_VALUE) {
2585
            adjustedPoint = vp.fromMapPoint(gridAdjustedPoint);
2586
            mapAdjustedPoint = gridAdjustedPoint;
2587
        } else {
2588
            mapAdjustedPoint = null;
2589
        }
2590

    
2591
        Point2D handlerAdjustedPoint = null;
2592

    
2593
        // Se comprueba el ajuste a los handlers
2594
        if (mapAdjustedPoint != null) {
2595
            handlerAdjustedPoint = (Point2D) mapAdjustedPoint.clone(); // getMapControl().getViewPort().toMapPoint(point);
2596
        } else {
2597
            handlerAdjustedPoint = vp.toMapPoint(point);
2598
        }
2599

    
2600
        Point2D mapPoint = new Point2D.Double();
2601
        double distance = adjustToHandler(handlerAdjustedPoint, mapPoint);
2602

    
2603
        if (distance < minDistance) {
2604
            bForceCoord = true;
2605
            adjustedPoint = vp.fromMapPoint(mapPoint);
2606
            mapAdjustedPoint = mapPoint;
2607
            minDistance = distance;
2608
        }
2609

    
2610
        // Si no hay ajuste
2611
        if (minDistance == Double.MAX_VALUE) {
2612
            adjustedPoint = point;
2613
            mapAdjustedPoint = null;
2614
        }
2615
    }
2616

    
2617
    /**
2618
     * Sets the snap tools enabled or disabled.
2619
     *
2620
     * @param activated
2621
     *            <code>true</code> to enable the snap tools; <code>false</code>
2622
     *            to disable them
2623
     *
2624
     * @see #isRefentEnabled()
2625
     */
2626
    public void setRefentEnabled(boolean activated) {
2627
        bRefent = activated;
2628
    }
2629

    
2630
    public Grid getGrid() {
2631
        return cadgrid;
2632
    }
2633

    
2634
    public void update(Observable observable, Object notification) {
2635
        DataStoreNotification ddsn = (DataStoreNotification) notification;
2636
        String type = ddsn.getType();
2637
        if (type.equals(FeatureStoreNotification.AFTER_UNDO)
2638
            || type.equals(FeatureStoreNotification.AFTER_REDO)) {
2639
            repaint();
2640
        }
2641
    }
2642

    
2643
    /**
2644
     * @return the adjustedPoint
2645
     */
2646
    public Point2D getAdjustedPoint() {
2647
        return adjustedPoint;
2648
    }
2649

    
2650
    /**
2651
     * @return the mapAdjustedPoint
2652
     */
2653
    public Point2D getMapAdjustedPoint() {
2654
        return mapAdjustedPoint;
2655
    }
2656

    
2657
    /**
2658
     * Gets the selected point. If the snapping is enabled
2659
     * it returns the selected point.
2660
     *
2661
     * @return
2662
     *         The selected point
2663
     */
2664
    public Point2D getPoint() {
2665
        if (mapAdjustedPoint != null) {
2666
            return mapAdjustedPoint;
2667
        } else {
2668
            return getViewPort().toMapPoint(adjustedPoint);
2669
        }
2670
    }
2671

    
2672
        public synchronized void dispose() {
2673
                // Check if we have already been disposed, and don't do it again
2674
                if (!disposed && ToolsLocator.getDisposableManager().release(this)) {
2675
                        drawer.setShutdown(true);
2676
                        disposed = true;
2677
                }
2678
        }
2679

    
2680
    public boolean isDefaultMouseWheelEnabled() {
2681
        return defaultMouseWheelEnabled;
2682
    }
2683

    
2684
    public void setDefaultMouseWheelEnabled(boolean defaultMouseWheelEnabled) {
2685
        this.defaultMouseWheelEnabled = defaultMouseWheelEnabled;
2686
    }
2687
    
2688
}