Statistics
| Revision:

root / branches / v2_0_0_prep / libraries / libFMap_mapcontrol / src / org / gvsig / fmap / mapcontrol / MapControl.java @ 22963

History | View | Annotate | Download (67.8 KB)

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

    
43
import java.awt.Color;
44
import java.awt.Component;
45
import java.awt.Dimension;
46
import java.awt.Graphics;
47
import java.awt.Graphics2D;
48
import java.awt.event.ActionEvent;
49
import java.awt.event.ActionListener;
50
import java.awt.event.ComponentEvent;
51
import java.awt.event.ComponentListener;
52
import java.awt.event.MouseEvent;
53
import java.awt.event.MouseListener;
54
import java.awt.event.MouseMotionListener;
55
import java.awt.event.MouseWheelEvent;
56
import java.awt.event.MouseWheelListener;
57
import java.awt.geom.Point2D;
58
import java.awt.image.BufferedImage;
59
import java.util.HashMap;
60
import java.util.Set;
61

    
62
import javax.swing.JComponent;
63
import javax.swing.Timer;
64

    
65
import org.cresques.cts.IProjection;
66
import org.gvsig.fmap.crs.CRSFactory;
67
import org.gvsig.fmap.data.DefaultDataStoreNotification;
68
import org.gvsig.fmap.geom.primitive.DefaultEnvelope;
69
import org.gvsig.fmap.geom.primitive.Envelope;
70
import org.gvsig.fmap.mapcontext.MapContext;
71
import org.gvsig.fmap.mapcontext.ViewPort;
72
import org.gvsig.fmap.mapcontext.events.AtomicEvent;
73
import org.gvsig.fmap.mapcontext.events.listeners.AtomicEventListener;
74
import org.gvsig.fmap.mapcontext.layers.FLayers;
75
import org.gvsig.fmap.mapcontext.layers.GraphicLayer;
76
import org.gvsig.fmap.mapcontext.layers.LayerCollectionEvent;
77
import org.gvsig.fmap.mapcontext.layers.LayerEvent;
78
import org.gvsig.fmap.mapcontrol.tools.BehaviorException;
79
import org.gvsig.fmap.mapcontrol.tools.CompoundBehavior;
80
import org.gvsig.fmap.mapcontrol.tools.Behavior.Behavior;
81
import org.gvsig.fmap.mapcontrol.tools.Listeners.ToolListener;
82
import org.gvsig.util.observer.Observable;
83
import org.gvsig.util.observer.Observer;
84

    
85
import com.iver.utiles.exceptionHandling.ExceptionHandlingSupport;
86
import com.iver.utiles.exceptionHandling.ExceptionListener;
87
import com.iver.utiles.swing.threads.Cancellable;
88

    
89

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

    
194
        /**
195
         * <p>One of the possible status of <code>MapControl</code>. Determines that not all visible information has been
196
         * drawn or isn't updated.</p>
197
         */
198
        public static final int DESACTUALIZADO = 1;
199

    
200
        /**
201
         * <p>One of the possible status of <code>MapControl</code>. Determines that only the graphical layer must
202
         * be drawn / updated.</p>
203
         */
204
        public static final int ONLY_GRAPHICS = 2;
205

    
206

    
207

    
208
    /**
209
     * <p>Determines if the drawer can update this <code>MapControl</code> instance when the timer launches an event.</p>
210
     */
211
    private static boolean drawAnimationEnabled = true;
212

    
213
    // public static final int FAST_PAINT = 3;
214
        //private static Logger logger = Logger.getLogger(MapControl.class.getName());
215

    
216
    /**
217
     * <p>Inner model with the layers, event support for drawing them, and the <code>ViewPort</code>
218
     *  with information to adapt to the bounds available in <i>image coordinates</i>.</p>
219
     *
220
     * @see #getMapContext()
221
     * @see #setMapContext(MapContext)
222
     */
223
    private MapContext mapContext = null;
224

    
225
    //private boolean drawerAlive = false;
226

    
227

    
228
        /**
229
         * <p>All registered <code>Behavior</code> that can define a way to work with this <code>MapControl</code>.</p>
230
         *
231
         * <p>Only one of them can be active at a given moment.</p>
232
         *
233
         * @see #addMapTool(String, Behavior)
234
         * @see #addMapTool(String, Behavior[])
235
         * @see #getMapTool(String)
236
         * @see #getMapToolsKeySet()
237
         * @see #getNamesMapTools()
238
         */
239
    protected HashMap namesMapTools = new HashMap();
240

    
241
        /**
242
         * <p>Active {@link Behavior Behavior} that will generate events according a criterion, and then, with a {@link ToolListener ToolListener}
243
         *  associated, will simulate to user that works with this component as a particular tool.</p>
244
         *
245
         * @see #getCurrentMapTool()
246
         * @see #getCurrentTool()
247
         * @see #setTool(String)
248
         */
249
    protected Behavior currentMapTool = null;
250

    
251
        /**
252
         * <p>Determines which's the current drawn status of this component:
253
         * <ul>
254
         *  <li><b>OUTDATED</b>: all visible information has been drawn or isn't updated.</li>
255
         *  <li><b>UTDATED</b>: all visible information has been drawn and its updated.</li>
256
         *  <li><b>ONLY_GRAPHICS</b>: only the graphical layer must be drawn / updated.</li>
257
         * </ul>
258
         * </p>
259
         *
260
         * <p>The <code>MapControl</code> drawing process will consider the value of this parameter to decide which elements will
261
         *  be updated or drawn.</p>
262
         */
263
    private int status = DESACTUALIZADO;
264

    
265
        /**
266
         * <p>Image with a buffer to accelerate the draw the changes of the graphical items in this component.</p>
267
         *
268
         * <p>Firstly, information will be drawn in the buffer, and, when is outright drawn, that information will be displayed.
269
         * Meanwhile, the previous image can be kept showed.</p>
270
         *
271
         * @see BufferedImage
272
         *
273
         * @see #getImage()
274
         */
275
    private BufferedImage image = null;
276

    
277
        /**
278
         * <p>Name of the tool used currently to interact with this component.</p>
279
         *
280
         * @see #getCurrentTool()
281
         * @see #setTool(String)
282
         */
283
    protected String currentTool;
284

    
285
        /**
286
         * <p>Object to store the flag that notifies a drawing thread task and <code>MapContext</code>'s layers,
287
         * that must be canceled or can continue with the process.</p>
288
         *
289
         * @see #cancelDrawing()
290
         */
291
    private CancelDraw canceldraw;
292

    
293
    //private boolean isCancelled = true;
294

    
295
        /**
296
         * <p>Fires an action events after a specified delay.</p>
297
         *
298
         * <p><code>MapControl</code> will use the timer to update its visible graphical information during
299
         *  a drawing process, or allowing to cancel that process.</p>
300
         *
301
         * <p>This is very useful to pretend faster interactivity to user when <code>MapControl</code> has
302
         *  lots of layers, and / or layers with heavy graphical elements, that need a long time to finish
303
         *  drawing all its data.</p>
304
         */
305
    private Timer timer;
306

    
307
        /**
308
         * <p>Reference to the {@link ViewPort ViewPort} of the {@link MapContext MapContext} of this component.</p>
309
         *
310
         * <p>The view port once is created an instance of <code>MapControl</code>,
311
         *  is obtained from the <i>EPSG:23030</i> projection, that's the default projection for this component.</p>
312
         *
313
         * <p>After, the view port will change adapting itself according the current projection and the extent.</p>
314
         *
315
         * @see #getViewPort()
316
         *
317
         * @see ViewPort
318
         */
319
    protected ViewPort vp;
320

    
321
    //private Drawer drawer;
322

    
323
        /**
324
         * <p>Manager of all <code>MapControl</code> painting requests.</p>
325
         */
326
    private Drawer2 drawer2;
327

    
328
    // private boolean firstDraw = true;
329

    
330
        /**
331
         * <p>Listener of all kind of mouse events produced in this component.</p>
332
         *
333
         * <p>Delegates each mouse event to the current map tool.</p>
334
         *
335
         * @see #addMapTool(String, Behavior)
336
         * @see #addMapTool(String, Behavior[])
337
         * @see #getMapTool(String)
338
         * @see #getMapToolsKeySet()
339
         * @see #getNamesMapTools()
340
         * @see #setTool(String)
341
         */
342
    protected MapToolListener mapToolListener = new MapToolListener();
343

    
344
        /**
345
         * <p>Listener of all events produced in a this component's <code>MapContext</code>
346
         * object during an atomic period of time.</p>
347
         */
348
    private MapContextListener mapContextListener = new MapContextListener();
349

    
350
        /**
351
         * <p>Group of <code>ExceptionListener</code> that, in whatever moment could be notified a Throwable Java error or exception.</p>
352
         *
353
         * @see #addExceptionListener(ExceptionListener)
354
         * @see #removeExceptionListener(ExceptionListener)
355
         */
356
    private ExceptionHandlingSupport exceptionHandlingSupport = new ExceptionHandlingSupport();
357

    
358
    /**
359
     * <p>Name of the previous tool used.</p>
360
     */
361
        protected String prevTool;
362

    
363
        /**
364
         * <p>Tool that will be used combined with the current tool of this <code>MapControl</code>.</p>
365
         */
366
        private Behavior combinedTool = null;
367

    
368
        /**
369
     * We need this to avoid not wanted refresh. REMEMBER TO SET TO TRUE!!
370
     */
371
    // private boolean paintEnabled = false;
372

    
373
        /**
374
         * <p>Creates a new <code>MapControl</code> instance with the following characteristics:
375
         * <ul>
376
         *  <li><i>Name</i>: MapControl .</li>
377
         *  <li>Disables the double buffer of <code>JComponent</code> .</li>
378
         *  <li>Sets opaque <i>(see {@link JComponent#setOpaque(boolean)} )</i>. </li>
379
         *  <li>Sets its status to <code>OUTDATED</code> .</li>
380
         *  <li>Creates a new {@link CancelDraw CancelDraw} object to notify <code>MapContext</code>'s layers if can continue processing the drawn or must cancel it.</li>
381
         *  <li>Creates a new {@link MapContext MapContext} with a new {@link ViewPort ViewPort} in the projection <i>"EPSG:23030"</i> .</li>
382
         *  <li>Creates a new {@link CommandListener CommandListener} for edition operations.</li>
383
         *  <li>Creates a new {@link MapToolListener MapToolListener}, and associates it as a listener of whatever kind of mouse events produced in this component.</li>
384
         *  <li>Creates a new {@link Drawer2 Drawer2} for managing the painting requests.</li>
385
         *  <li>Creates a new timer that will invoke refresh this component <code>drawFrameRate</code> per second, when is running a drawing process, and its enabled
386
         *   <code>drawAnimationEnabled</code>.</li>
387
         * </ul>
388
         * </p>
389
         */
390
        public MapControl() {
391
                this.setName("MapControl");
392
                setDoubleBuffered(false);
393
                setOpaque(true);
394
                status = DESACTUALIZADO;
395

    
396
                //Clase usada para cancelar el dibujado
397
                canceldraw = new CancelDraw();
398

    
399
                //Modelo de datos y ventana del mismo
400
                // TODO: Cuando creamos un mapControl, deber?amos asignar
401
                // la projecci?n por defecto con la que vayamos a trabajar.
402
                // 23030 es el c?digo EPSG del UTM30 elipsoide ED50
403
                vp = new ViewPort(CRSFactory.getCRS("EPSG:23030"));
404
                setMapContext(new MapContext(vp));
405

    
406
                //eventos
407
                this.addComponentListener(this);
408
                this.addMouseListener(mapToolListener);
409
                this.addMouseMotionListener(mapToolListener);
410
                this.addMouseWheelListener(mapToolListener);
411

    
412
        this.drawer2 = new Drawer2();
413
                //Timer para mostrar el redibujado mientras se dibuja
414
                timer = new Timer(1000/MapContext.getDrawFrameRate(),
415
                                new ActionListener() {
416
                                        public void actionPerformed(ActionEvent e) {
417

    
418
                                                if (drawAnimationEnabled) {
419
                                                        MapControl.this.repaint();
420
                                                }
421
                                        }
422
                                });
423
        }
424

    
425
        /**
426
         * <p>Sets a <code>MapContext</code> to this component.</p>
427
         *
428
         * <p>The <code>MapContext</code> has the <i>model</i>, and most of the <i>view</i>,
429
         * and <i>control</i> logic of the layers of this component, including a {@link ViewPort ViewPort} to adapt the
430
         * information to the projection, and to display it in the available area.</p>
431
         *
432
         * <p>If <code>model</code> hadn't a <code>ViewPort</code>, assigns the current one to it, otherwise, use its <code>ViewPort</code>.</p>
433
         *
434
         * <p>After assigning the <code>MapContext</code> and <code>ViewPort</code>, sets the same {@link MapContextListener MapContextListener}
435
         *  that was using, and changes the <i>status</i> to <code>OUTDATED</code>.</p>
436
         *
437
         * @param model this component's <code>MapContext</code>, that includes the <code>ViewPort</code>.
438
         *
439
         * @see MapContext
440
         *
441
         * @see #getMapContext()
442
         */
443
        public void setMapContext(MapContext model) {
444
                if (mapContext != null) {
445
                        mapContext.removeAtomicEventListener(mapContextListener);
446
                }
447

    
448
                mapContext = model;
449

    
450
                if (mapContext.getViewPort() == null) {
451
                        mapContext.setViewPort(vp);
452
                } else {
453
                        vp = mapContext.getViewPort();
454

    
455
                        // vp.setImageSize(new Dimension(getWidth(), getHeight()));
456
                        //System.err.println("Viewport en setMapContext:" + vp);
457
                }
458

    
459
                mapContext.addAtomicEventListener(mapContextListener);
460

    
461
                status = DESACTUALIZADO;
462
        }
463

    
464
        /**
465
         * <p>Gets this component's {@link MapContext MapContext} projection.</p>
466
         *
467
         * @return this component's {@link MapContext MapContext} projection
468
         *
469
         * @see MapContext#getProjection()
470
         * @see MapControl#setProjection(IProjection)
471
         */
472
        public IProjection getProjection() {
473
                return getMapContext().getProjection();
474
        }
475

    
476
        /**
477
         * <p>Sets the projection to this component's {@link MapContext MapContext}.</p>
478
         *
479
         * @param proj the kind of projection to this component's {@link MapContext MapContext}
480
         *
481
         * @see MapContext#setProjection(IProjection)
482
         * @see MapControl#getProjection()
483
         */
484
        public void setProjection(IProjection proj) {
485
                getMapContext().setProjection(proj);
486
        }
487

    
488
        /**
489
         * <p>Gets this component's <code>MapContext</code>, with the <i>model</i>, and most of the <i>view</i>,
490
         * and <i>control</i> logic of the layers of this component, including a {@link ViewPort ViewPort} to adapt the
491
         * information to the projection, and display it in the available area.</p>
492
         *
493
         * @return this component's <code>MapContext</code>, that includes the <code>ViewPort</code> used to project the
494
         * graphical information, and display it in the available area
495
         *
496
         * @see MapContext
497
         *
498
         * @see MapControl#setMapContext(MapContext)
499
         */
500
        public MapContext getMapContext() {
501
                return mapContext;
502
        }
503

    
504
        /**
505
         * <p>Registers a new behavior to this component.</p>
506
         *
507
         * <p>According the nature of the {@link Behavior Behavior}, different events will be generated. Those
508
         *  events can be caught by a particular {@link ToolListener ToolListener}, allowing user to interact with this
509
         *  <code>MapControl</code> object as a <i>tool</i>.</p>
510
         *
511
         * @param name name to identify the behavior to add
512
         * @param tool the behavior to add
513
         *
514
         * @see #addMapTool(String, Behavior[])
515
         * @see #getNamesMapTools()
516
         * @see #getMapToolsKeySet()
517
         * @see #hasTool(String)
518
         */
519
        public void addMapTool(String name, Behavior tool) {
520
                namesMapTools.put(name, tool);
521
                tool.setMapControl(this);
522
        }
523

    
524
        /**
525
         * <p>Registers a new behavior to this component as a {@link CompoundBehavior CompoundBehavior} made up of <code>tools</code>.</p>
526
         *
527
         * <p>According the nature of the behaviors registered, different events will be generated. Those
528
         *  events can be caught by a particular {@link ToolListener ToolListener}, allowing user to interact with this
529
         *  <code>MapControl</code> object as a <i>tool</i>.</p>
530
         *
531
         * @param name name to identify the compound behavior to add
532
         * @param tools the compound behavior to add
533
         *
534
         * @see #addMapTool(String, Behavior)
535
         * @see #getNamesMapTools()
536
         * @see #getMapToolsKeySet()
537
         * @see #hasTool(String)
538
         */
539
        public void addMapTool(String name, Behavior[] tools){
540
                CompoundBehavior tool = new CompoundBehavior(tools);
541
                addMapTool(name, tool);
542
        }
543

    
544
        /**
545
         * <p>Gets the <code>Behavior</code> registered in this component, identified
546
         *  by <code>name</code>.</p>
547
         *
548
         * @param name name of a registered behavior
549
         *
550
         * @return tool the registered behavior in this component as <code>name</code>, or <code>null</code> if
551
         *  no one has that identifier
552
         *
553
         * @see #addMapTool(String, Behavior)
554
         * @see #addMapTool(String, Behavior[])
555
         * @see #hasTool(String)
556
         */
557
        public Behavior getMapTool(String name) {
558
                return (Behavior)namesMapTools.get(name);
559
        }
560

    
561
        /**
562
         * <p>Returns a set view of the keys that identified the tools
563
         *  registered.</p>
564
         *
565
         * @return a set view of the keys that identified the tools registered
566
         *
567
         * @see HashMap#keySet()
568
         *
569
         * @see #getNamesMapTools()
570
          * @see #addMapTool(String, Behavior)
571
          * @see #addMapTool(String, Behavior[])
572
         */
573
        public Set getMapToolsKeySet() {
574
                return namesMapTools.keySet();
575
        }
576

    
577
        /**
578
         * <p>Returns <code>true</code> if this component contains a tool identified by <code>toolName</code>.</p>
579
         *
580
         * @param toolName identifier of the tool
581
         *
582
         * @return <code>true</code> if this component contains a tool identified by <code>toolName</code>; otherwise <code>false</code>
583
         *
584
         * @see #addMapTool(String, Behavior)
585
         * @see #addMapTool(String, Behavior[])
586
         */
587
        public boolean hasTool(String toolName) {
588
                return namesMapTools.containsKey(toolName);
589
        }
590

    
591
        /**
592
         * <p>Sets as current active <code>Behavior</code> associated to this component, that one which
593
         *  is registered and identified by <code>toolName</code>.</p>
594
         *
595
         * <p>Changing the current active behavior for this <code>MapControl</code>, implies also updating the
596
         *  previous <i>behavior</i> tool, and the current cursor.</p>
597
         *
598
         * @param toolName name of a registered behavior
599
         *
600
         * @see #getCurrentMapTool()
601
         * @see #getCurrentTool()
602
         */
603
        public void setTool(String toolName) {
604
                prevTool=getCurrentTool();
605
                Behavior mapTool = (Behavior) namesMapTools.get(toolName);
606
                currentMapTool = mapTool;
607
                currentTool = toolName;
608

    
609
                if (combinedTool != null) {
610
                        if (mapTool instanceof CompoundBehavior) {
611
                                ((CompoundBehavior)mapTool).addMapBehavior(combinedTool, true);
612
                        }
613
                        else {
614
                                currentMapTool = new CompoundBehavior(new Behavior[] {currentMapTool});
615
                                ((CompoundBehavior)currentMapTool).addMapBehavior(combinedTool, true);
616
                        }
617
                }
618

    
619
                this.setCursor(mapTool.getCursor());
620
        }
621

    
622
        /**
623
         * <p>Gets as current active <code>Behavior</code> associated to this component, that one which
624
         *  is registered and identified by <code>toolName</code>.</p>
625
         *
626
         * <p>Changing the current active behavior for this <code>MapControl</code>, implies also updating the
627
         *  previous <i>behavior</i> tool, and the current cursor.</p>
628
         *
629
         * @param toolName name of a registered behavior
630
         *
631
         * @see #getCurrentTool()
632
         * @see #setTool(String)
633
         */
634
        public Behavior getCurrentMapTool(){
635
                return currentMapTool;
636
        }
637

    
638
        /**
639
         * <p>Returns the name of the current selected tool on this MapControl</p>
640
         *
641
         * @return the name of the current's behavior tool associated to this component
642
         *
643
         * @see #getCurrentMapTool()
644
         * @see #setTool(String)
645
         */
646
        public String getCurrentTool() {
647
                return currentTool;
648
        }
649

    
650
        /**
651
         * <p>Determines that current drawing process of <code>MapControl</code>'s <code>MapContext</code>'s data must be canceled.</p>
652
         *
653
         * <p>It has no effects if now isn't drawing that graphical information.</p>
654
         *
655
         * <p>At last resort, the particular implementation of each layer in this <code>MapControl</code>'s <code>MapContrext</code>
656
     *   will be that one which will draw the graphical information, and, if supports, which could cancel its drawing subprocess.</p>
657
         */
658
        public void cancelDrawing() {
659
                /* if (drawer != null) {
660
                        if (!drawer.isAlive()) {
661
                                return;
662
                        }
663
                }
664
                */
665
                canceldraw.setCanceled(true);
666

    
667
                /* while (!isCancelled) {
668
                        if (!drawer.isAlive()) {
669
                            // Si hemos llegado aqu? con un thread vivo, seguramente
670
                            // no estamos actualizados.
671

672
                                break;
673
                        }
674

675
                }
676
                canceldraw.setCancel(false);
677
                isCancelled = false;
678
        drawerAlive = false; */
679
        }
680

    
681
        /**
682
         * <p>Creates a {@link BufferedImage BufferedImage} image if there was no buffered image, or if
683
         *  its viewport's image height or width is different from this component's size. Once has created
684
         *  a double-buffer, fills it with the vieport's background color, or with <i>white</i> if it had no background color.</p>
685
         *
686
         * <p>If no double-buffered existed, creates a {@link BufferedImage BufferedImage} with the size of this component,
687
         * and as an image with 8-bit RGBA color components packed into integer pixels. That image has a <code>DirectColorModel</code> with alpha.
688
         * The color data in that image is considered not to be premultiplied with alpha.</p>
689
         *
690
         * <p>Once has created and filled the new inner <code>MapControl</code>'s double-buffer, changes the status to
691
         * <code>OUTDATED</code>.</p>
692
         *
693
         * @return <code>true</code> if has created and filled a new double-buffer for this <code>MapControl</code> instance; otherwise <code>false</code>
694
         */
695
    private boolean adaptToImageSize()
696
    {
697
        if ((image == null) || (vp.getImageWidth() != this.getWidth()) || (vp.getImageHeight() != this.getHeight()))
698
        {
699
            image = new BufferedImage(this.getWidth(), this.getHeight(),
700
                    BufferedImage.TYPE_INT_ARGB);
701
            // ESTILO MAC
702
//                image = GraphicsEnvironment.getLocalGraphicsEnvironment()
703
//                                .getDefaultScreenDevice().getDefaultConfiguration()
704
//                                .createCompatibleImage(this.getWidth(), this.getHeight());
705
            vp.setImageSize(new Dimension(getWidth(), getHeight()));
706
            getMapContext().getViewPort().refreshExtent();
707

    
708

    
709
            Graphics gTemp = image.createGraphics();
710
            Color theBackColor = vp.getBackColor();
711
            if (theBackColor == null)
712
                gTemp.setColor(Color.WHITE);
713
            else
714
                gTemp.setColor(theBackColor);
715

    
716
            gTemp.fillRect(0,0,getWidth(), getHeight());
717
            gTemp.dispose();
718
            status = DESACTUALIZADO;
719
            // g.drawImage(image,0,0,null);
720
            return true;
721
        }
722
        return false;
723
    }
724

    
725
        /**
726
         * <p>Paints the graphical information of this component using a double buffer.</p>
727
         *
728
         * <p>If the double buffer wasn't created, creates a new one.</p>
729
         *
730
         * <p>Paints the component according the following algorithm:
731
         * <br>
732
         *  &nbsp If <i>status</i> is <i>UPDATED</i>:<br>
733
         *  &nbsp &nbsp If there is no <i>double buffer</i>:<br>
734
         *  &nbsp &nbsp &nbsp If there is a <i>behavior</i> for managing the <code>MapControl</code> instance, delegates
735
         *   the drawing process to that behavior, calling: <code><i>behavior_instance</i>.paintComponent(g)</code> &nbsp .<br>
736
         *  &nbsp &nbsp &nbsp Else, repaints the current graphical information quickly calling: <code>g.drawImage(image,0,0,null)</code> &nbsp .<br>
737
         *  &nbsp Else, (<i>status</i> is <i>OUTDATED</i>, or <i>ONLY_GRAPHICS</i>): executes a quickly repaint of the previous information calling <code>g.drawImage(image,0,0,null)</code>, and creates
738
         *   a <i>painting request</i> to delegate the heavy drawing process to the {@link Drawer2 Drawer2}'s worker thread, according the <i>SingleWorketThread</i> pattern, starting a timer to update
739
         *   (invoking <code>repaint()</code> that comprises invoke this method) the view every delay of 360 ms. during the the process drawing.</p>
740
         *
741
           * @see javax.swing.JComponent#paintComponent(java.awt.Graphics)
742
           * @see Drawer2
743
         */
744
        protected void paintComponent(Graphics g) {
745
        adaptToImageSize();
746
        /* if (status == FAST_PAINT) {
747
            System.out.println("FAST_PAINT");
748
            g.drawImage(image,0,0,null);
749
            status = ACTUALIZADO;
750
            return;
751
        } */
752
        // System.out.println("PINTANDO MAPCONTROL" + this);
753
                if (status == ACTUALIZADO) {
754
                        // LWS logger.debug("Dibujando la imagen obtenida");
755

    
756
                        /*
757
                         * Si hay un behaviour y la imagen es distinta de null se delega el dibujado
758
                         * en dicho behaviour
759
                         */
760
            if (image != null)
761
            {
762
                if (currentMapTool != null)
763
                    currentMapTool.paintComponent(g);
764
                else
765
                    g.drawImage(image,0,0,null);
766

    
767
                                // System.out.println("Pinto ACTUALIZADO");
768
                        }
769
                } else if ((status == DESACTUALIZADO)
770
                || (status == ONLY_GRAPHICS)) {
771
                        // LWS System.out.println("DESACTUALIZADO: Obteniendo la imagen de la cartograf?a");
772
                        /* if (isOpaque())
773
                        {
774
                            if (image==null)
775
                            {
776
                                g.setColor(vp.getBackColor());
777
                                g.fillRect(0,0,getWidth(), getHeight());
778
                            }
779
                            // else g.drawImage(image,0,0,null);
780
                        } */
781
            // cancelDrawing();
782
                        //Se crea la imagen con el color de fonde deseado
783
                        /* if (image == null)
784
                        {
785
                                image = new BufferedImage(this.getWidth(), this.getHeight(),
786
                                                BufferedImage.TYPE_INT_ARGB);
787
                                vp.setImageSize(new Dimension(getWidth(), getHeight()));
788
                                Graphics gTemp = image.createGraphics();
789
                            Color theBackColor = vp.getBackColor();
790
                            if (theBackColor == null)
791
                                gTemp.setColor(Color.WHITE);
792
                            else
793
                                gTemp.setColor(theBackColor);
794

795
                                gTemp.fillRect(0,0,getWidth(), getHeight());
796
                                gTemp.dispose();
797
                                // g.drawImage(image,0,0,null);
798
                                System.out.println("Imagen con null en DESACTUALIZADO. Width = " + this.getWidth());
799
                        } */
800
            // else
801
            // {
802

    
803

    
804
            // if (image != null)
805
            //  {
806
                g.drawImage(image,0,0,null);
807

    
808
                drawer2.put(new PaintingRequest());
809
                timer.start();
810
            /* }
811
            else
812
                return; */
813
            // }
814

    
815
            /* if (drawerAlive == false)
816
            {
817
                drawer = new Drawer(image, canceldraw);
818
                drawer.start();
819
                        //Se lanza el tread de dibujado
820
            } */
821

    
822
                        // status = ACTUALIZADO;
823
                }
824
        }
825

    
826
        /**
827
         * <p>Gets the {@link BufferedImage BufferedImage} used to accelerate the draw of new ''frames'' with changes,
828
         * or new graphical items in this component.</p>
829
         *
830
         * @return double buffered image used by this component to accelerate the draw of its graphical information, or
831
         * <code>null</code> if isn't already created
832
         *
833
         * @see BufferedImage
834
         */
835
        public BufferedImage getImage() {
836
                return image;
837
        }
838

    
839
        /**
840
         * <p>Forces repaint all visible graphical information in this component.</p>
841
         *
842
         * <p>If <code>doClear == true</code>, before repainting, clears the background color, with the
843
         *  inner viewport's background color.</p>
844
         *
845
         * @param doClear <code>true</code> if needs clearing the background color before drawing the map
846
         *
847
         * @see #cancelDrawing()
848
         * @see FLayers#setDirty(boolean)
849
         */
850
        public void drawMap(boolean doClear) {
851
                cancelDrawing();
852
                //System.out.println("drawMap con doClear=" + doClear);
853
        status = DESACTUALIZADO;
854
        getMapContext().getLayers().setDirty(true);
855
                if (doClear)
856
        {
857
            // image = null; // Se usa para el PAN
858
            if (image != null)
859
            {
860
                Graphics2D g = image.createGraphics();
861
                Color theBackColor = vp.getBackColor();
862
                if (theBackColor == null)
863
                    g.setColor(Color.WHITE);
864
                else
865
                    g.setColor(theBackColor);
866
                g.fillRect(0, 0, vp.getImageWidth(), vp.getImageHeight());
867
                g.dispose();
868
            }
869
        }
870
                repaint();
871
        }
872

    
873

    
874
        /**
875
         * <p>Cancels any current drawing process, changing the status to <code>OUTDATED</code>, and forcing
876
         * repaint only the layers dirty.</p>
877
         *
878
         * @see #cancelDrawing()
879
         */
880
        public void rePaintDirtyLayers()
881
        {
882
                cancelDrawing();
883
        status = DESACTUALIZADO;
884
        repaint();
885
        }
886

    
887
        /**
888
         * <p>Cancels any current drawing process, changing the status to <code>ONLY_GRAPHICS</code>, and forcing
889
         * repaint only the graphical layer of the <code>MapContext</code>.</p>
890
         */
891
    public void drawGraphics() {
892
        status = ONLY_GRAPHICS;
893
        repaint();
894
    }
895

    
896
        /**
897
         * @see java.awt.event.ComponentListener#componentHidden(java.awt.event.ComponentEvent)
898
         */
899
        public void componentHidden(ComponentEvent e) {
900
        }
901

    
902
        /**
903
         * @see java.awt.event.ComponentListener#componentMoved(java.awt.event.ComponentEvent)
904
         */
905
        public void componentMoved(ComponentEvent e) {
906
        }
907

    
908
        /**
909
         * @see java.awt.event.ComponentListener#componentResized(java.awt.event.ComponentEvent)
910
         */
911
        public void componentResized(ComponentEvent e) {
912
                /* image = new BufferedImage(this.getWidth(), this.getHeight(),
913
                                BufferedImage.TYPE_INT_ARGB);
914
                Graphics gTemp = image.createGraphics();
915
                gTemp.setColor(vp.getBackColor());
916
                gTemp.fillRect(0,0,getWidth(), getHeight());
917
        System.out.println("MapControl resized");
918
            // image = null;
919
            vp.setImageSize(new Dimension(getWidth(), getHeight()));
920
                getMapContext().getViewPort().setScale(); */
921
                // drawMap(true);
922
        }
923

    
924
        /**
925
         * @see java.awt.event.ComponentListener#componentShown(java.awt.event.ComponentEvent)
926
         */
927
        public void componentShown(ComponentEvent e) {
928
        }
929

    
930
        /**
931
         * @see ExceptionHandlingSupport#addExceptionListener(ExceptionListener)
932
         */
933
        public void addExceptionListener(ExceptionListener o) {
934
                exceptionHandlingSupport.addExceptionListener(o);
935
        }
936

    
937
        /**
938
         * @see ExceptionHandlingSupport#removeExceptionListener(ExceptionListener)
939
         */
940
        public boolean removeExceptionListener(ExceptionListener o) {
941
                return exceptionHandlingSupport.removeExceptionListener(o);
942
        }
943

    
944
        /**
945
         * @see ExceptionHandlingSupport#throwException(Throwable)
946
         */
947
        protected void throwException(Throwable t) {
948
                exceptionHandlingSupport.throwException(t);
949
        }
950

    
951
        /**
952
         * <p>Represents each <code>MapControl</code>'s data painting request.</p>
953
         *
954
         * <p>The request will be attended by a <code>Drawer2</code>, which will hold it since the <code>Drawer2</code>'s worker
955
         *  takes it, or arrives a new painting request, which will replace it.</p>
956
         */
957
    private class PaintingRequest
958
    {
959
            /**
960
             * <p>Creates a new <code>PaintingRequest</p> instance.</p>
961
             */
962
        public PaintingRequest()
963
        {
964
        }
965

    
966
        /**
967
         * <p><code>MapControl</code> paint process:</p>
968
         *
969
         * <p>
970
         *  <ul>
971
         *   <li><i>1.- </i>Cancels all previous <code>MapControl</code>'s drawing processes.</li>
972
         *   <li><i>2.- </i>If <i>status</i> was OUTDATED:
973
         *    <ul>
974
         *     <li><i>2.1.- </i>Fills the background color with viewport's background color, or <i>white</i> if it was undefined.</li>
975
         *     <li><i>2.2.- </i>Notifies <i>MapContext</i> to be drawn invoking: <code>mapContext.draw(double-buffer, double-buffer's buffer, shared cancel-draw object, mapContext.getScaleView());</code>.</li>
976
         *     <li><i>2.3.- </i>If <code>canceldraw.isCanceled()</code>
977
         *      <ul>
978
         *       <li><i>2.3.1.- </i>Sets <i>status</i> to OUTDATED.</li>
979
         *       <li><i>2.3.2.- </i>Sets <i>dirty</i> all layers stored in <i>MapContext</i>.</li>
980
         *      </ul>
981
         *     </li>
982
         *     <li><i>2.4.- </i>Else, sets <i>status</i> to UPDATED.</li>
983
         *    </ul>
984
         *   </li>
985
         *   <li><i>3.- </i>Else, if <i>status</i> was ONLY_GRAPHICS:
986
         *    <ul>
987
         *     <li><i>3.1.- </i>Sets <i>status</i> to UPDATED.</li>
988
         *     <li><i>3.2.- </i>Notifies <i>MapContext</i> to be drawn invoking: <code>mapContext.drawGraphics(double-buffer, double-buffer's buffer, shared cancel-draw object, mapContext.getScaleView());</code>.</li>
989
         *    </ul>
990
         *   </li>
991
         *   <li><i>4.- </i>Stops the <i>timer</i>.</li>
992
         *   <li><i>5.- </i>Repaints this component invoking: <code>repaint();</code></li>
993
         *  </ul>
994
         * </p>
995
         *
996
         * @see #cancelDrawing()
997
         * @see MapContext#draw(BufferedImage, Graphics2D, Cancellable, double)
998
         * @see MapContext#drawGraphics(BufferedImage, Graphics2D, Cancellable, double)
999
         *
1000
         * @see ViewPort
1001
         */
1002
        public void paint()
1003
        {
1004
            try
1005
            {
1006
                    canceldraw.setCanceled(false);
1007
                /* if (image == null)
1008
                {
1009
                    image = new BufferedImage(vp.getImageWidth(), vp.getImageHeight(),
1010
                            BufferedImage.TYPE_INT_ARGB);
1011
                    Graphics gTemp = image.createGraphics();
1012
                    Color theBackColor = vp.getBackColor();
1013
                    if (theBackColor == null)
1014
                        gTemp.setColor(Color.WHITE);
1015
                    else
1016
                        gTemp.setColor(theBackColor);
1017

1018
                    gTemp.fillRect(0,0,getWidth(), getHeight());
1019
                    gTemp.dispose();
1020
                    // g.drawImage(image,0,0,null);
1021
                    System.out.println("Imagen con null en DESACTUALIZADO. Width = " + this.getWidth());
1022
                } */
1023
                Graphics2D g = image.createGraphics();
1024

    
1025
                ViewPort viewPort = mapContext.getViewPort();
1026

    
1027
                if (status == DESACTUALIZADO)
1028
                {
1029
                        Graphics2D gTemp = image.createGraphics();
1030
                    Color theBackColor = viewPort.getBackColor();
1031
                    if (theBackColor == null)
1032
                        gTemp.setColor(Color.WHITE);
1033
                    else
1034
                        gTemp.setColor(theBackColor);
1035
                    gTemp.fillRect(0, 0, viewPort.getImageWidth(), viewPort.getImageHeight());
1036
                    // ESTILO MAC
1037
//                  BufferedImage imgMac = new BufferedImage(vp.getImageWidth(), vp.getImageHeight(),
1038
//                          BufferedImage.TYPE_INT_ARGB);
1039
          //
1040
//                  mapContext.draw(imgMac, g, canceldraw, mapContext.getScaleView());
1041
//                  g.drawImage(imgMac, 0, 0, null);
1042
                  // FIN ESTILO MAC
1043
                  // SIN MAC:
1044

    
1045
                  mapContext.draw(image, g, canceldraw, mapContext.getScaleView());
1046
                  if (!canceldraw.isCanceled()){
1047
                          status=ACTUALIZADO;
1048
                 }else{
1049
                          status=DESACTUALIZADO;
1050
                          getMapContext().getLayers().setDirty(true);
1051
                  }
1052

    
1053
                }
1054
                else if (status == ONLY_GRAPHICS)
1055
                {
1056
                    status = ACTUALIZADO;
1057
                    mapContext.drawGraphics(image, g, canceldraw,mapContext.getScaleView());
1058

    
1059
                }
1060

    
1061

    
1062
                // status = FAST_PAINT;
1063
              //  drawerAlive = false;
1064
                timer.stop();
1065
                repaint();
1066

    
1067

    
1068
            } catch (Throwable e) {
1069
                timer.stop();
1070
              //  isCancelled = true;
1071
                e.printStackTrace();
1072
                throwException(e);
1073
            } finally {
1074
            }
1075
        }
1076
    }
1077

    
1078
    /**
1079
     * <p>An instance of <code>Drawer2</code> could manage all <code>MapControl</code> painting requests.</p>
1080
     *
1081
     * <p>Based on the <i>WorkerThread</i> software pattern, creates a worker thread that will attend sequentially
1082
     *  the current waiting painting request, after finishing the previous (that could be by a cancel action).</p>
1083
     *
1084
     * <p>All new {@link PaintingRequest PaintingRequest} generated will be stored as <i>waiting requests</i> since the worker
1085
     * attends it.</p>
1086
     *
1087
     * <p>If a worker finished and there was no <i>painting request</i>, the worker would be set to wait until any
1088
     *  <i>painting request</i> would be put.</p>
1089
     *
1090
     * @author fjp
1091
     */
1092
    public class Drawer2
1093
    {
1094
        // Una mini cola de 2. No acumulamos peticiones de dibujado
1095
        // dibujamos solo lo ?ltimo que nos han pedido.
1096

    
1097

    
1098
            /**
1099
             * <p>Painting request that's been attended by the <code>Drawer2</code>'s worker.</p>
1100
             *
1101
             * @see #put(org.gvsig.fmap.mapcontrol.MapControl.PaintingRequest)
1102
             * @see #take()
1103
             */
1104
            private PaintingRequest paintingRequest;
1105

    
1106
            /**
1107
             * <p>Painting request waiting to be attended by the <code>Drawer2</code>'s worker.</p>
1108
             *
1109
             * @see #put(org.gvsig.fmap.mapcontrol.MapControl.PaintingRequest)
1110
             * @see #take()
1111
             */
1112
            private PaintingRequest waitingRequest;
1113

    
1114
            /**
1115
             * <p>Determines that the <code>Drawer2</code>'s worker is busy attending a painting request.</p>
1116
              *
1117
             * @see #put(org.gvsig.fmap.mapcontrol.MapControl.PaintingRequest)
1118
             * @see #take()
1119
             */
1120
        private boolean waiting;
1121

    
1122
            /**
1123
             * <p>Notifies the <code>Drawer2</code>'s worker to finish or continue with its process.</p>
1124
             *
1125
             * @see #setShutdown(boolean)
1126
             */
1127
        private boolean shutdown;
1128

    
1129
        /**
1130
         * <p>Sets this <code>Drawer2</code>'s worker to finish or continue with its process.</p>
1131
         *
1132
         * @param isShutdown a boolean value
1133
         */
1134
        public void setShutdown(boolean isShutdown)
1135
        {
1136
            shutdown = isShutdown;
1137
        }
1138

    
1139
        /**
1140
         * <p>Creates a new drawer for managing all data painting requests in <code>MapControl</code>.</p>
1141
         *
1142
         * <p>Includes the following steps:
1143
         *  <ul>
1144
         *   <li>By default, there is no <i>current painting request</i>.</li>
1145
         *   <li>By default, there is no <i>waiting painting request</i>.</li>
1146
         *   <li>By default, the worker thread is waiting no <i>painting request</i>.</li>
1147
         *   <li>By default, the worker thread is running.</li>
1148
         *   <li>Creates and starts a worker thread for attending the <i>painting requests</i>.</li>
1149
         *  </ul>
1150
         * </p>
1151
         */
1152
        public Drawer2()
1153
        {
1154
            paintingRequest = null;
1155
            waitingRequest = null;
1156
            waiting = false;
1157
            shutdown = false;
1158
            new Thread(new Worker()).start();
1159
        }
1160

    
1161
        /**
1162
         * <p>Sets a <code>PaintingRequest</code> to be attended by the worker thread of this object. If
1163
         *  this one was waiting, wakes up.</p>
1164
         *
1165
         * <p>All waiting threads will be notified synchronized.</p>
1166
         *
1167
         * @param newPaintRequest
1168
         *
1169
         * @see #take()
1170
         */
1171
        public void put(PaintingRequest newPaintRequest)
1172
        {
1173
            waitingRequest = newPaintRequest;
1174
            if (waiting)
1175
            {
1176
                synchronized (this) {
1177
                    notifyAll();
1178
                }
1179
            }
1180
        }
1181

    
1182
        /**
1183
         * <p>Used by this object's worker, returns the current waiting drawing request, causing current thread
1184
         *  to wait until another thread invokes {@link #put(org.gvsig.fmap.mapcontrol.MapControl.PaintingRequest) #put(com.iver.cit.gvsig.fmap.MapControl.PaintingRequest)},
1185
         *  if there was no waiting request.</p>
1186
         *
1187
         * <p>All threads will access synchronized to the waiting request.</p>
1188
         *
1189
         * @return <code>PaintingRequest</code> that was waiting to be attended
1190
         *
1191
         * @see #put(org.gvsig.fmap.mapcontrol.MapControl.PaintingRequest)
1192
         */
1193
        public PaintingRequest take()
1194
        {
1195
            if (waitingRequest == null)
1196
            {
1197
                synchronized (this) {
1198
                    waiting = true;
1199
                    try {
1200
                        wait();
1201
                    }
1202
                    catch (InterruptedException ie)
1203
                    {
1204
                        waiting = false;
1205
                    }
1206
                }
1207
            }
1208
            paintingRequest = waitingRequest;
1209
            waitingRequest = null;
1210
            return paintingRequest;
1211
        }
1212

    
1213
        /**
1214
         * <p>Thread for attending painting requests.</p>
1215
         *
1216
         * <p>If there was no double buffer, sets the status to <code>OUTDATED</code> and finishes, otherwise
1217
         *  takes the painting request (it's probably that would wait some time), cancel the previous drawing
1218
         *  process, and starts processing the request.</p>
1219
         *
1220
         * @see Thread
1221
         */
1222
        private class Worker implements Runnable
1223
        {
1224
                /*
1225
                 * (non-Javadoc)
1226
                 * @see java.lang.Runnable#run()
1227
                 */
1228
                public void run()
1229
            {
1230
                while (!shutdown)
1231
                {
1232
                    PaintingRequest p = take();
1233
                    //System.out.println("Pintando");
1234
                    if (image != null){
1235
                            cancelDrawing();
1236
                        p.paint();
1237
                    } else{
1238
                        status = DESACTUALIZADO;
1239
                    }
1240
                }
1241
            }
1242
        }
1243
    }
1244

    
1245
        /**
1246
         * <p><code>Drawer</code> is implemented for drawing the layers of a <code>MapControl</code>'s <i>MapContext</i> instance
1247
         *  as a <i>thread</i> of execution.</p>
1248
         *
1249
         * <p>Draws <code>MapControl</code> according its <i>status</i>:
1250
         *  <ul>
1251
         *   <li><code><i>ONLY_GRAPHICS</i></code>: refreshes only the graphical layer, changing the status to <code><i>UPDATED</i></code>, via
1252
         *    {@linkplain MapContext#drawGraphics(BufferedImage, Graphics2D, Cancellable, double) MapContext#drawGraphics(BufferedImage, Graphics2D, Cancellable, double)}.</li>
1253
         *   <li><code><i>OUTDATED</i></code>: refreshes all layers, changing the status to <code><i>UPDATED</i></code>, via
1254
         *    {@linkplain MapContext#draw(BufferedImage, Graphics2D, Cancellable, double) MapContext#draw(BufferedImage, Graphics2D, Cancellable, double)}.</li>
1255
         *  <ul>
1256
         * </p>
1257
         *
1258
         * <p>This drawing process is accelerated by a <code>BufferedImage</code>, and can be canceled.</p>
1259
         *
1260
         * <p>Once the drawing process has finished, the timer stops and this component gets repainted.</p>
1261
         *
1262
         * @deprecated
1263
         * @author Vicente Caballero Navarro
1264
         */
1265
        public class Drawer extends Thread {
1266
                //private Graphics g;
1267

    
1268
                /**
1269
                 * <p>Image with a buffer to accelerate the draw the changes of the graphical items in this component.</p>
1270
                 *
1271
                 * <p>Firstly, information will be drawn in the buffer, and, when is outright drawn, that information will be displayed.
1272
                 * Meanwhile, the previous image can be kept showed.</p>
1273
                 *
1274
                 * @see BufferedImage
1275
                 */
1276
                private BufferedImage image = null;
1277

    
1278
                /**
1279
                 * <p>Object to store the flag that notifies the drawing must be canceled or can continue with the process.</p>
1280
                 *
1281
                 * <p>At last resort, the particular implementation of each layer in a <code>MapControl</code>'s <code>MapContrext</code>
1282
         *   will be which will draw the graphical information, and, if supports, which could cancel its drawing subprocess.</p>
1283
                 */
1284
                private CancelDraw cancel;
1285
                //private boolean threadCancel = false;
1286

    
1287
                /**
1288
                 * <p>Creates a new <code>Drawer</code> instance.</p>
1289
                 */
1290
                public Drawer(BufferedImage image, CancelDraw cancel)
1291
        {
1292
                        this.image = image;
1293
                        this.cancel = cancel;
1294
         //   drawerAlive = true;
1295
                }
1296

    
1297
                /**
1298
                 * @see java.lang.Runnable#run()
1299
                 * @see MapContext#draw(BufferedImage, Graphics2D, Cancellable, double)
1300
                 * @see MapContext#drawGraphics(BufferedImage, Graphics2D, Cancellable, double)
1301
                 */
1302
                public void run() {
1303
                        try {
1304
                                // synchronized (Drawer.class) {
1305
                                    Graphics2D g = image.createGraphics();
1306

    
1307
                                    ViewPort viewPort = mapContext.getViewPort();
1308
                    if (status == DESACTUALIZADO)
1309
                    {
1310
                                        Color theBackColor = viewPort.getBackColor();
1311
                                        if (theBackColor == null)
1312
                                            g.setColor(Color.WHITE);
1313
                                        else
1314
                                            g.setColor(theBackColor);
1315
                                            g.fillRect(0, 0, viewPort.getImageWidth(), viewPort.getImageHeight());
1316
                        status = ACTUALIZADO;
1317
                        mapContext.draw(image, g, cancel,mapContext.getScaleView());
1318
                    }
1319
                    else if (status == ONLY_GRAPHICS)
1320
                    {
1321
                        status = ACTUALIZADO;
1322
                        mapContext.drawGraphics(image, g, cancel,mapContext.getScaleView());
1323
                    }
1324

    
1325
                                        timer.stop();
1326
                    // status = FAST_PAINT;
1327
                  //  drawerAlive = false;
1328
                                        repaint();
1329

    
1330

    
1331

    
1332
                                // }
1333
                        } catch (Throwable e) {
1334
                            timer.stop();
1335
                                //isCancelled = true;
1336
                e.printStackTrace();
1337
                                throwException(e);
1338
                        } finally {
1339
                        }
1340
                }
1341
        }
1342

    
1343
        /**
1344
         * <p>An instance of <code>CancelDraw</code> will be shared by all this <code>MapControl</code>'s <code>MapContext</code> layers,
1345
         *  allowing receive a notification that, when they're been drawn, to be cancelled.</p>
1346
         *
1347
         * @see Cancellable
1348
         *
1349
         * @author Fernando Gonz?lez Cort?s
1350
         */
1351
        public class CancelDraw implements Cancellable {
1352
                /**
1353
                 * <p>Determines if the drawing task must be canceled or not.</p>
1354
                 *
1355
                 * @see #isCanceled()
1356
                 * @see #setCanceled(boolean)
1357
                 */
1358
                private boolean cancel = false;
1359

    
1360
                /**
1361
                 * Creates a new <code>CancelDraw</code> object.
1362
                 */
1363
                public CancelDraw() {
1364
                }
1365

    
1366
                /*
1367
                 * (non-Javadoc)
1368
                 * @see com.iver.utiles.swing.threads.Cancellable#setCanceled(boolean)
1369
                 */
1370
                public void setCanceled(boolean b) {
1371
                        cancel = b;
1372
                }
1373

    
1374
                /*
1375
                 * (non-Javadoc)
1376
                 * @see com.iver.utiles.swing.threads.Cancellable#isCanceled()
1377
                 */
1378
                public boolean isCanceled() {
1379
                        return cancel;
1380
                }
1381
        }
1382

    
1383
        /**
1384
         * <p>Listens all kind of mouse events produced in {@link MapControl MapControl}, and invokes its current
1385
         *  map tool <i>({@link MapControl#getCurrentMapTool() MapControl#getCurrentMapTool()}</i> to simulate a behavior.</p>
1386
         *
1387
         * <p>Mouse wheel moved events produce a <i>zoom in</i> operation if wheel rotation is negative, or a <i>zoom out</i>
1388
         *  if its positive. Both will be centered in the position of the mouse, but, meanwhile <i>zoom in</i> operation
1389
         *  applies a factor of 0.9, <i>zoom out</i> operation applies a factor of 1.2</p>
1390
         *
1391
         * <p>Mouse wheel moved events can be produced as much frequently, that between each one, the drawing process could
1392
         *  hadn't finished. This is the reason that, in this situation, cancels always the previous drawing process before
1393
         *  applying a <i>zoom</i> operation, and ignores all new mouse positions that are produced before 1 second.</p>
1394
         *
1395
         * @author Fernando Gonz?lez Cort?s
1396
         */
1397
        public class MapToolListener implements MouseListener, MouseWheelListener,
1398
                MouseMotionListener {
1399

    
1400
                /**
1401
                 * <p>Used to avoid mouse wheel move events closed.</p>
1402
                 *
1403
                 * <p>If a mouse wheel move event is produced
1404
                 */
1405
                long t1;
1406

    
1407
                /**
1408
                 * <p>Position of the mouse, in map coordinates.</p>
1409
                 *
1410
                 * <p>This point coordinates will be used as center of the <i>zoom</i> operation.</p>
1411
                 */
1412
                Point2D pReal;
1413

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

    
1427
                /**
1428
                 * @see java.awt.event.MouseListener#mouseEntered(java.awt.event.MouseEvent)
1429
                 * @see Behavior#mouseEntered(MouseEvent)
1430
                 */
1431
                public void mouseEntered(MouseEvent e) {
1432
                        try {
1433
                                if (currentMapTool != null)
1434
                                        currentMapTool.mouseEntered(e);
1435
                        } catch (BehaviorException t) {
1436
                                throwException(t);
1437
                        }
1438
                }
1439

    
1440
                /**
1441
                 * @see java.awt.event.MouseListener#mouseExited(java.awt.event.MouseEvent)
1442
                 * @see Behavior#mouseExited(MouseEvent)
1443
                 */
1444
                public void mouseExited(MouseEvent e) {
1445
                        try {
1446
                                if (currentMapTool != null)
1447
                                    currentMapTool.mouseExited(e);
1448
                        } catch (BehaviorException t) {
1449
                                throwException(t);
1450
                        }
1451
                }
1452

    
1453
                /**
1454
                 * @see java.awt.event.MouseListener#mousePressed(java.awt.event.MouseEvent)
1455
                 * @see Behavior#mousePressed(MouseEvent)
1456
                 */
1457
                public void mousePressed(MouseEvent e) {
1458
                        try {
1459
                                if (currentMapTool != null)
1460
                                        currentMapTool.mousePressed(e);
1461
                        } catch (BehaviorException t) {
1462
                                throwException(t);
1463
                        }
1464
                }
1465

    
1466
                /**
1467
                 * @see java.awt.event.MouseListener#mouseReleased(java.awt.event.MouseEvent)
1468
                 * @see Behavior#mouseReleased(MouseEvent)
1469
                 */
1470
                public void mouseReleased(MouseEvent e) {
1471
                        try {
1472
                                if (currentMapTool != null)
1473
                                        currentMapTool.mouseReleased(e);
1474
                        } catch (BehaviorException t) {
1475
                                throwException(t);
1476
                        }
1477
                }
1478

    
1479
                /**
1480
                 * @see java.awt.event.MouseWheelListener#mouseWheelMoved(java.awt.event.MouseWheelEvent)
1481
                 * @see Behavior#mouseWheelMoved(MouseWheelEvent)
1482
                 */
1483
                public void mouseWheelMoved(MouseWheelEvent e) {
1484
                        try {
1485
                                if (currentMapTool == null)
1486
                                        return;
1487

    
1488
                                currentMapTool.mouseWheelMoved(e);
1489

    
1490
                                // Si el tool actual no ha consumido el evento
1491
                                // entendemos que quiere el comportamiento por defecto.
1492
                                if (!e.isConsumed())
1493
                                {
1494
                                        // Para usar el primer punto sobre el que queremos centrar
1495
                                        // el mapa, dejamos pasar un segundo para considerar el siguiente
1496
                                        // punto como v?lido.
1497
                                        if (t1 == 0)
1498
                                        {
1499
                                                t1= System.currentTimeMillis();
1500
                                                pReal = vp.toMapPoint(e.getPoint());
1501
                                        }
1502
                                        else
1503
                                        {
1504
                                                long t2 = System.currentTimeMillis();
1505
                                                if ((t2-t1) > 1000)
1506
                                                        t1=0;
1507
                                        }
1508
                                        cancelDrawing();
1509
                                        ViewPort vp = getViewPort();
1510

    
1511

    
1512
                                        /* Point2D pReal = new Point2D.Double(vp.getAdjustedExtent().getCenterX(),
1513
                                                        vp.getAdjustedExtent().getCenterY()); */
1514
                                        int amount = e.getWheelRotation();
1515
                                        double nuevoX;
1516
                                        double nuevoY;
1517
                                        double factor;
1518

    
1519
                                        if (amount < 0) // nos acercamos
1520
                                        {
1521
                                                factor = 0.9;
1522
                                        } else // nos alejamos
1523
                                        {
1524
                                                factor = 1.2;
1525
                                        }
1526
                                        if (vp.getExtent() != null) {
1527
                                                nuevoX = pReal.getX()
1528
                                                                - ((vp.getExtent().getWidth() * factor) / 2.0);
1529
                                                nuevoY = pReal.getY()
1530
                                                                - ((vp.getExtent().getHeight() * factor) / 2.0);
1531
                                                double x = nuevoX;
1532
                                                double y = nuevoY;
1533
                                                double width = vp.getExtent().getWidth() * factor;
1534
                                                double height = vp.getExtent().getHeight() * factor;
1535

    
1536
                                                vp.setEnvelope(new DefaultEnvelope(2,new double[]{x,y},new double[]{x+width,y+height}));
1537
                                        }
1538

    
1539

    
1540
                                }
1541
                        } catch (BehaviorException t) {
1542
                                throwException(t);
1543
                        }
1544
                }
1545

    
1546
                /**
1547
                 * @see java.awt.event.MouseMotionListener#mouseDragged(java.awt.event.MouseEvent)
1548
                 * @see Behavior#mouseDragged(MouseEvent)
1549
                 */
1550
                public void mouseDragged(MouseEvent e) {
1551
                        try {
1552
                                if (currentMapTool != null)
1553
                                        currentMapTool.mouseDragged(e);
1554
                        } catch (BehaviorException t) {
1555
                                throwException(t);
1556
                        }
1557
                }
1558

    
1559
                /**
1560
                 * @see java.awt.event.MouseMotionListener#mouseMoved(java.awt.event.MouseEvent)
1561
                 * @see Behavior#mouseMoved(MouseEvent)
1562
                 */
1563
                public void mouseMoved(MouseEvent e) {
1564
                        try {
1565
                                if (currentMapTool != null)
1566
                                        currentMapTool.mouseMoved(e);
1567
                        } catch (BehaviorException t) {
1568
                                throwException(t);
1569
                        }
1570
                }
1571
        }
1572

    
1573
        /**
1574
         * <p<code>MapContextListener</code> listens all events produced in a <code>MapControl</code>'s <code>MapContext</code>
1575
         * object during an atomic period of time, and sets it to dirty, <i>executing <code>drawMap(false)</code>, if any of the
1576
         * following conditions is accomplished</i>:
1577
         * <ul>
1578
         *  <li>Any of the <code>LayerEvent</code> in the <code>AtomicEvent</code> parameter notifies a <i>visibility change</i>.</li>
1579
         *  <li>There is at least one <code>ColorEvent</code> in the <code>AtomicEvent</code> parameter.</li>
1580
         *  <li>There is at least one <code>ExtentEvent</code> in the <code>AtomicEvent</code> parameter.</li>
1581
         *  <li>Any of the <code>LayerCollectionEvent</code> in the <code>AtomicEvent</code> parameter notifies that a driver's layer has reloaded it successfully.</li>
1582
         *  <li>There is at least one <code>LegendEvent</code> in the <code>AtomicEvent</code> parameter.</li>
1583
         *  <li>There is at least one <code>SelectionEvent</code> in the <code>AtomicEvent</code> parameter.</li>
1584
         * </ul>
1585
         * </p>
1586
         *
1587
         * @author Fernando Gonz?lez Cort?s
1588
         */
1589
        public class MapContextListener implements AtomicEventListener {
1590
                /**
1591
                 * @see org.gvsig.fmap.mapcontext.events.listeners.AtomicEventListener#atomicEvent(org.gvsig.fmap.mapcontext.events.AtomicEvent)
1592
                 */
1593
                public void atomicEvent(AtomicEvent e) {
1594
                        boolean redraw = false;
1595
                        LayerEvent[] layerEvents = e.getLayerEvents();
1596

    
1597
                        for (int i = 0; i < layerEvents.length; i++) {
1598
                                if (layerEvents[i].getProperty().equals("visible")) {
1599
                                        redraw = true;
1600
                                }
1601
                        }
1602

    
1603
                        if (e.getColorEvents().length > 0) {
1604
                                redraw = true;
1605
                        }
1606

    
1607
                        if (e.getExtentEvents().length > 0) {
1608
                                redraw = true;
1609
                        }
1610

    
1611
                        if (e.getProjectionEvents().length > 0) {
1612
                                //redraw = true;
1613
                        }
1614

    
1615

    
1616
                        LayerCollectionEvent[] aux = e.getLayerCollectionEvents();
1617
                        if (aux.length > 0) {
1618
                                for (int i=0; i < aux.length; i++)
1619
                                {
1620
                                        if (aux[i].getAffectedLayer().getFLayerStatus().isDriverLoaded())
1621
                                        {
1622
                                                redraw = true;
1623
                                                break;
1624
                                        }
1625
                                }
1626

    
1627
                        }
1628

    
1629
                        if (e.getLegendEvents().length > 0) {
1630
                                redraw = true;
1631
                        }
1632

    
1633
                        if (e.getSelectionEvents().length > 0) {
1634
                                redraw = true;
1635
                        }
1636

    
1637
                        if (redraw) {
1638
                //System.out.println("MapContextListener redraw");
1639
                                MapControl.this.drawMap(false);
1640
                        }
1641
                }
1642
        }
1643

    
1644
        /**
1645
         * <p>Gets the <code>ViewPort</code> of this component's {@link MapContext MapContext} .</p>
1646
         *
1647
         * @see MapContext#getViewPort()
1648
         */
1649
        public ViewPort getViewPort() {
1650
                return vp;
1651
        }
1652

    
1653
        /**
1654
         * <p>Returns all registered <code>Behavior</code> that can define a way to work with this <code>MapControl</code>.</p>
1655
         *
1656
         * @return registered <code>Behavior</code> to this <code>MapControl</code>
1657
         *
1658
         * @see #addMapTool(String, Behavior)
1659
         * @see #addMapTool(String, Behavior[])
1660
         * @see #getMapToolsKeySet()
1661
         * @see #hasTool(String)
1662
         */
1663
        public HashMap getNamesMapTools() {
1664
                return namesMapTools;
1665
        }
1666

    
1667
        /*
1668
         * (non-Javadoc)
1669
         * @see com.iver.cit.gvsig.fmap.edition.commands.CommandListener#commandRepaint()
1670
         */
1671
        public void commandRepaint() {
1672
                drawMap(false);
1673
        }
1674

    
1675
        /*
1676
         * (non-Javadoc)
1677
         * @see com.iver.cit.gvsig.fmap.edition.commands.CommandListener#commandRefresh()
1678
         */
1679
        public void commandRefresh() {
1680
                // TODO Auto-generated method stub
1681
        }
1682

    
1683
        /**
1684
         * <p>Equivalent operation to <i>undo</i>.</p>
1685
         *
1686
         * <p>Exchanges the previous tool with the current one.</p>
1687
         *
1688
         * @see #addMapTool(String, Behavior)
1689
         * @see #addMapTool(String, Behavior[])
1690
         * @see #setTool(String)
1691
         */
1692
        public void setPrevTool() {
1693
                setTool(prevTool);
1694
        }
1695

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

    
1721
        /**
1722
         * <p>Executes a <i>zoom out</i> operation centered at the center of the extent.</p>
1723
         *
1724
         * <p>This implementation is thought for being invoked outside a <code>Behavior</code>, for example
1725
         *  by an action of pressing a button, and simulates that the event has been produced by
1726
         *  releasing the <i>button 1</i> of the mouse using the registered <code>Behavior</code> in this
1727
         *  <code>MapControl</code> object that's responsible for the <i>zoom out</i> operation.</p>
1728
         *
1729
         * @see #zoomIn()
1730
         */
1731
        public void zoomOut() {
1732
                getMapContext().clearAllCachingImageDrawnLayers();
1733
                Behavior mapTool = (Behavior) namesMapTools.get("zoomOut");
1734
                ViewPort vp=getViewPort();
1735
                Envelope r=getViewPort().getAdjustedExtent();
1736
                Point2D pCenter=vp.fromMapPoint(r.getCenter(0),r.getCenter(1));
1737
                MouseEvent e=new MouseEvent((Component)this,MouseEvent.MOUSE_RELEASED,MouseEvent.ACTION_EVENT_MASK,MouseEvent.BUTTON1,(int)pCenter.getX(),(int)pCenter.getY(),1,true,MouseEvent.BUTTON1);
1738
                try {
1739
                        mapTool.mousePressed(e);
1740
                        mapTool.mouseReleased(e);
1741
                } catch (BehaviorException t) {
1742
                        throwException(t);
1743
                }
1744
        }
1745

    
1746
        /**
1747
         * <p>Returns the listener used to catch all mouse events produced in this <code>MapControl</code> instance
1748
         *  and that redirects the calls to the current map tool.</p>
1749
         *
1750
         * @return the map tool listener used
1751
         */
1752
        public MapToolListener getMapToolListener() {
1753
                return mapToolListener;
1754
        }
1755

    
1756
//         mapTool can be null, for instance, in 3D's navigation tools
1757
        /**
1758
         * <p>Sets <code>mapTool</code> as this <code>MapControl</code>'s current map tool.
1759
         *
1760
         * @param mapTool a map tool, or <code>null</code> to disable the interaction with the user
1761
         *
1762
         * @see #getCurrentMapTool()
1763
         * @see #getCurrentTool()
1764
         * @see #setTool(String)
1765
         * @see #setPrevTool()
1766
         * @see #addMapTool(String, Behavior)
1767
         * @see #addMapTool(String, Behavior[])
1768
         */
1769
        public void setCurrentMapTool(Behavior mapTool ){
1770
                currentMapTool = mapTool;
1771
        }
1772

    
1773
        /**
1774
         * <p>Sets the delay to the timer that refreshes this <code>MapControl</code> instance.</p>
1775
         *
1776
         * <p><code>Delay (in ms) = 1000 / getDrawFrameRate()</code></p>
1777
         *
1778
         * @see #getDrawFrameRate()
1779
         * @see #setDrawFrameRate(int)
1780
         */
1781
        public void applyFrameRate() {
1782
                if (MapContext.getDrawFrameRate()>0) {
1783
                        timer.setDelay(1000/MapContext.getDrawFrameRate());
1784
                }
1785
        }
1786

    
1787

    
1788

    
1789
        /**
1790
         * <p>Determines if its enabled the repaint that invokes the timer according to {@link #getDrawFrameRate() #getDrawFrameRate()}.</p>
1791
         *
1792
         * @return <code>true</code> if its enabled; otherwise <code>false</code>
1793
         */
1794
        public static boolean isDrawAnimationEnabled() {
1795
                return drawAnimationEnabled;
1796
        }
1797

    
1798
        /**
1799
         * <p>Sets if its enabled the repaint that invokes the timer according to {@link #getDrawFrameRate() #getDrawFrameRate()}.</p>
1800
         *
1801
         * @param drawAnimationEnabled <code>true</code> to enable the mode; otherwise <code>false</code>
1802
         */
1803
        public static void setDrawAnimationEnabled(boolean drawAnimationEnabled) {
1804
                MapControl.drawAnimationEnabled = drawAnimationEnabled;
1805
        }
1806

    
1807
        /**
1808
         * <p>Gets the shared object that determines if a drawing process must be cancelled or can continue.</p>
1809
         *
1810
         * @return the shared object that determines if a drawing process must be cancelled or can continue
1811
         */
1812
        public CancelDraw getCanceldraw() {
1813
                return canceldraw;
1814
        }
1815

    
1816
        /**
1817
         * <p>Gets the tool used in combination with the current tool of this <code>MapControl</code>.</p>
1818
         *
1819
         * @return the tool used in combination with the <code>currentMapTool</code>; <code>null</code> if there is
1820
         *  no combined tool
1821
         */
1822
        public Behavior getCombinedTool() {
1823
                return combinedTool;
1824
        }
1825

    
1826
        /**
1827
         * <p>Sets a tool to be used in combination with the current tool of this <code>MapControl</code>.</p>
1828
         *
1829
         * @param combinedTool a tool to be used in combination with the current tool of <code>MapControl</code>
1830
         */
1831
        public void setCombinedTool(Behavior combinedTool) {
1832
                this.combinedTool = combinedTool;
1833

    
1834
                if (currentMapTool == null)
1835
                        return;
1836

    
1837
                if (currentMapTool instanceof CompoundBehavior) {
1838
                        ((CompoundBehavior)currentMapTool).addMapBehavior(combinedTool, true);
1839
                }
1840
                else {
1841
                        currentMapTool = new CompoundBehavior(new Behavior[] {currentMapTool});
1842
                        ((CompoundBehavior)currentMapTool).addMapBehavior(combinedTool, true);
1843
                }
1844

    
1845
        }
1846
        /**
1847
         * <p>Adds a new tool as combined tool.</p>
1848
         * <p>The new tool will be stored with the previous combined tools, and will be combined with
1849
         *  the current tool.</p>
1850
         * <p>If <code>tool</code> was already stored as a combined tool, doesn't adds it.</p>
1851
         *
1852
         * @param tool a new tool to be used combined with the current tool
1853
         */
1854
        public void addCombinedTool(Behavior tool) {
1855
                if (combinedTool == null) {
1856
                        combinedTool = tool;
1857
                }
1858
                else {
1859
                        if (combinedTool instanceof CompoundBehavior) {
1860
                                if (((CompoundBehavior)combinedTool).containsBehavior(tool))
1861
                                        return;
1862

    
1863
                                ((CompoundBehavior)combinedTool).addMapBehavior(tool, true);
1864
                        }
1865
                        else {
1866
                                if (combinedTool.equals(tool))
1867
                                        return;
1868

    
1869
                                combinedTool = new CompoundBehavior(new Behavior[] {combinedTool});
1870
                                ((CompoundBehavior)combinedTool).addMapBehavior(tool, true);
1871
                        }
1872
                }
1873

    
1874
                if (currentMapTool == null)
1875
                        return;
1876

    
1877
                if (currentMapTool instanceof CompoundBehavior) {
1878
                        ((CompoundBehavior)currentMapTool).addMapBehavior(tool, true);
1879
                }
1880
                else {
1881
                        currentMapTool = new CompoundBehavior(new Behavior[] {currentMapTool});
1882
                        ((CompoundBehavior)currentMapTool).addMapBehavior(tool, true);
1883
                }
1884
        }
1885
        /**
1886
         * <p>Removes the tool <code>tool</code> used in combination with the current tool of this <code>MapControl</code>.</p>
1887
         */
1888
        public void removeCombinedTool(Behavior tool) {
1889
                if ((currentMapTool != null) && (currentMapTool instanceof CompoundBehavior)) {
1890
                                ((CompoundBehavior)currentMapTool).removeMapBehavior(tool);
1891
                }
1892

    
1893
                if (combinedTool == null)
1894
                        return;
1895

    
1896
                if (combinedTool instanceof CompoundBehavior) {
1897
                        ((CompoundBehavior)combinedTool).removeMapBehavior(tool);
1898
                }
1899
                else {
1900
                        combinedTool = null;
1901
                }
1902
        }
1903

    
1904
        public void update(Observable observable, Object notification) {
1905
                DefaultDataStoreNotification ddsn=(DefaultDataStoreNotification)notification;
1906
                String type=ddsn.getType();
1907
                if (type.equals(DefaultDataStoreNotification.AFTER_UNDO) ||
1908
                                type.equals(DefaultDataStoreNotification.AFTER_REDO)){
1909
                        repaint();
1910
                }
1911
        }
1912
}