Statistics
| Revision:

svn-document-layout / trunk / org.gvsig.app.document.layout2.app / org.gvsig.app.document.layout2.app.mainplugin / src / main / java / org / gvsig / app / project / documents / layout / fframes / FFrameView.java @ 762

History | View | Annotate | Download (58.7 KB)

1
/* gvSIG. Geographic Information System of the Valencian Government
2
 *
3
 * Copyright (C) 2007-2008 Infrastructures and Transports Department
4
 * of the Valencian Government (CIT)
5
 *
6
 * This program is free software; you can redistribute it and/or
7
 * modify it under the terms of the GNU General Public License
8
 * as published by the Free Software Foundation; either version 2
9
 * of the License, or (at your option) any later version.
10
 *
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License
17
 * along with this program; if not, write to the Free Software
18
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19
 * MA  02110-1301, USA.
20
 *
21
 */
22
package org.gvsig.app.project.documents.layout.fframes;
23

    
24
import java.awt.BasicStroke;
25
import java.awt.Color;
26
import java.awt.Dimension;
27
import java.awt.Font;
28
import java.awt.Graphics2D;
29
import java.awt.Point;
30
import java.awt.Rectangle;
31
import java.awt.geom.AffineTransform;
32
import java.awt.geom.Point2D;
33
import java.awt.geom.Rectangle2D;
34
import java.awt.image.BufferedImage;
35

    
36
import org.cresques.cts.IProjection;
37
import org.gvsig.andami.PluginServices;
38
import org.gvsig.andami.ui.mdiFrame.NewStatusBar;
39
import org.gvsig.app.project.Project;
40
import org.gvsig.app.project.ProjectManager;
41
import org.gvsig.app.project.documents.layout.DefaultLayoutNotification;
42
import org.gvsig.app.project.documents.layout.FLayoutFunctions;
43
import org.gvsig.app.project.documents.layout.FLayoutUtilities;
44
import org.gvsig.app.project.documents.layout.LayoutNotification;
45
import org.gvsig.app.project.documents.layout.TocModelChangedNotification;
46
import org.gvsig.app.project.documents.view.ViewDocument;
47
import org.gvsig.compat.print.PrintAttributes;
48
import org.gvsig.fmap.dal.exception.ReadException;
49
import org.gvsig.fmap.geom.Geometry;
50
import org.gvsig.fmap.geom.Geometry.SUBTYPES;
51
import org.gvsig.fmap.geom.GeometryLocator;
52
import org.gvsig.fmap.geom.GeometryManager;
53
import org.gvsig.fmap.geom.exception.CreateEnvelopeException;
54
import org.gvsig.fmap.geom.primitive.Envelope;
55
import org.gvsig.fmap.mapcontext.MapContext;
56
import org.gvsig.fmap.mapcontext.MapContextException;
57
import org.gvsig.fmap.mapcontext.ViewPort;
58
import org.gvsig.fmap.mapcontext.events.AtomicEvent;
59
import org.gvsig.fmap.mapcontext.events.ColorEvent;
60
import org.gvsig.fmap.mapcontext.events.ExtentEvent;
61
import org.gvsig.fmap.mapcontext.events.FMapEvent;
62
import org.gvsig.fmap.mapcontext.events.ProjectionEvent;
63
import org.gvsig.fmap.mapcontext.events.listeners.AtomicEventListener;
64
import org.gvsig.fmap.mapcontext.events.listeners.ViewPortListener;
65
import org.gvsig.fmap.mapcontext.layers.CancelationException;
66
import org.gvsig.fmap.mapcontext.layers.LayerCollectionEvent;
67
import org.gvsig.fmap.mapcontext.layers.LayerCollectionListener;
68
import org.gvsig.fmap.mapcontext.layers.LayerEvent;
69
import org.gvsig.fmap.mapcontext.layers.LayerPositionEvent;
70
import org.gvsig.fmap.mapcontext.rendering.legend.events.LegendChangedEvent;
71
import org.gvsig.fmap.mapcontext.rendering.legend.events.listeners.LegendListener;
72
import org.gvsig.gui.beans.Messages;
73
import org.gvsig.tools.ToolsLocator;
74
import org.gvsig.tools.dynobject.DynStruct;
75
import org.gvsig.tools.persistence.PersistenceManager;
76
import org.gvsig.tools.persistence.PersistentState;
77
import org.gvsig.tools.persistence.exception.PersistenceException;
78
import org.slf4j.Logger;
79
import org.slf4j.LoggerFactory;
80

    
81
/**
82
 * FFrame used for embedding a View in the Layout. The View is not actually
83
 * inserted on the Layout, but it is used together with the MapContext in order
84
 * do draw an image which is then painted on the FFrame. Therefore, no
85
 * MapControl is used in current implementation, but a similar behavior is
86
 * simulated by the FFrameView. The original MapContext is cloned when assigned
87
 * to the FFrameView, which is then used for drawing.
88
 *
89
 * The FFrameView and the associated View can be synchronized, depending on the
90
 * values of {@link #getTypeScale()} and {@link #getLinked()}.
91
 *
92
 * The main synchronization logic is kept on two internal classes:
93
 * {@link OwnMapContextListener} and {@link ViewDocListener}. The first one
94
 * listens for events on the FFrameView and synchronizes the View accordingly.
95
 * The second one listens for events on the View and synchronizes the FFrameView
96
 * accordingly. There synchronization process is flagged using
97
 * {@link #b_updating} and {@link #b_updating}, in order to avoid incurring on
98
 * infinite synchronization loops.
99
 *
100
 */
101
public class FFrameView extends FFrame implements IFFrameUseProject,
102
        IFFrameUseFMap, LayoutPanelListener {
103

    
104
    private static final Logger logger = LoggerFactory.getLogger(FFrameView.class);
105

    
106
    public static final String PERSISTENCE_DEFINITION_NAME = "FFrameView";
107

    
108
    private static final String QUALITY_FIELD = "quality";
109
    private static final String MAPUNITS_FIELD = "mapUnits";
110
    private static final String SCALE_FIELD = "scale";
111
    private static final String VIEW_FIELD = "view";
112
    private static final String ENVELOPE_FIELD = "envelope";
113
    private static final String SHOWGRID_FIELD = "showGrid";
114
    private static final String GRID_FIELD = "gridview";
115
    private static final String HAS_TOC_FIELD = "hasToc";
116
    private static final String LAYER_SYNC_FIELD = "layerSync";
117
    private static final String EXTENT_SYNC_FIELD = "extentSync";
118
    private static final String SCALE_TYPE_FIELD = "scaleType";
119

    
120
    // following fields are unused - they are kept for backward-compatibility
121
    private static final String EXTENSION_FIELD = "extension";
122
    private static final String BLINKED_FIELD = "bLinked";
123
    private static final String MODE_FIELD = "mode";
124
    private static final String TYPESCALE_FIELD = "typeScale";
125
    private static final String MAPCONTEXT_FIELD = "mapContext";
126
    private static final String VIEWING_FIELD = "viewing";
127

    
128
    public static final int PRESENTATION = 0;
129
    public static final int DRAFT = 1;
130

    
131
    protected boolean syncLayers = true;
132
    protected boolean syncExtents = true;
133
    protected int quality = PRESENTATION;
134
    protected ViewDocument viewDocument = null;
135
    protected MapContext mapContext = null;
136
    protected int mapUnits = 1; // Meters.
137

    
138
    protected BufferedImage m_image = null;
139
    protected AffineTransform mapAT = null;
140
    protected Project project = null;
141
    protected double scaleAnt;
142
    protected Point origin;
143
    protected Point2D p1;
144
    protected Point2D p2;
145
    protected IFFrame grid;
146
    protected boolean showGrid = false;
147

    
148
    private boolean b_updating = false;
149
    protected boolean b_validCache = false;
150
    protected boolean b_drawing = false;
151
    private ViewDocListener viewDocListener;
152
    private OwnMapContextListener ownMapContextListener;
153
    private boolean b_hasToc = true;
154
    protected static final GeometryManager geomManager = GeometryLocator.getGeometryManager();
155

    
156
    /**
157
     * When we load a FFrameview from project, it will receive a wrong
158
     * extentChanged event the first time the View is painted, so we must ignore
159
     * this first event.
160
     */
161
    private boolean b_frameInitialized = true;
162

    
163
    protected AffineTransform originalGraphicsAT = null;
164
    protected Rectangle originalClip = null;
165

    
166
    private SCALE_TYPE scaleType = SCALE_TYPE.NORMAL;
167
    private Double fixedScale = null;
168
    private Envelope fixedExtent = null;
169

    
170
    /**
171
     * Creates a new FFrameView object.
172
     */
173
    public FFrameView() {
174
        num++;
175
//        logger.info("Create FFrameView "+ Integer.toHexString(this.hashCode()));
176
        createListeners();
177
    }
178

    
179
    /**
180
     * Returns a description of the FFrame
181
     *
182
     * @return Description.
183
     */
184
    @Override
185
    public String toString() {
186
        if (getView() == null) {
187
            return "FFrameView " + num + ": " + "Vacio";
188
        }
189
        return "FFrameView " + num + ": " + getView().getName();
190
    }
191

    
192
    /**
193
     * Sets the scale of the MapContext contained in this FFrameView
194
     *
195
     * @param d Scale to be set
196
     */
197
    @Override
198
    public void setScale(double d) {
199
        if (getMapContext() != null) {
200
            getMapContext().setScaleView((long) d);
201
        }
202
    }
203

    
204
    /**
205
     * Sets a new Envelope on the MapContext contained in this FFrameView
206
     *
207
     * @param r Envelope to be set
208
     */
209
    @Override
210
    public void setNewEnvelope(Envelope r) {
211
        getMapContext().getViewPort().setEnvelope(r);
212
        updateScaleCtrl();
213
    }
214

    
215
    /**
216
     * Calculates the resolution (measured on dots per inch, DPI) to be
217
     * considered to draw the FFrameView on screen. It is calculated by dividing
218
     * the width (in pixels) of the FFrame divided by the width in inches of the
219
     * paper.
220
     *
221
     * @return
222
     */
223
    protected double getDrawPaperDPI() {
224
        AffineTransform at = null;
225
        if (getLayoutContext() != null) {
226
            at = getLayoutContext().getAT();
227
        }
228
        return (2.54 * getBoundingBox(at).width) / getBoundBox().width;
229
    }
230

    
231
    /**
232
     * Returns the MapContext contained in this FFrameView, which is usually a
233
     * clone of the associated View. This MapContext may be synchronized with
234
     * the View one, depending on the scale type that has been set (see
235
     * {{@link #getTypeScale()}.
236
     *
237
     * @return The mapContext object
238
     */
239
    @Override
240
    public MapContext getMapContext() {
241
        return mapContext;
242
    }
243

    
244
    /**
245
     * Sets the quality of the visualization of the FFrame on screen. Valid
246
     * values include {@link #DRAFT} and {@link #PRESENTATION}. Draft will
247
     * disable the frame normal drawing, which will be replaced by an empty
248
     * rectangle only showing the name of the frame.
249
     *
250
     * @param q Integer representing the quality.
251
     */
252
    public void setQuality(int q) {
253
        quality = q;
254
    }
255

    
256
    /**
257
     * Gets the quality of the visualization of the FFrame on screen. Valid
258
     * values include {@link #DRAFT} and {@link #PRESENTATION}. Draft will
259
     * disable the frame normal drawing, which will be replaced by an empty
260
     * rectangle only showing the name of the frame.
261
     *
262
     * @return
263
     */
264
    public int getQuality() {
265
        return quality;
266
    }
267

    
268
    /**
269
     * Sets the MapContext associated with this FFrameView, which will be used
270
     * to clone the layers and synchronize the FFrameView with the associated
271
     * View
272
     *
273
     * @param viewMapContext
274
     */
275
    public void setViewMapContext(MapContext viewMapContext) {
276
        Envelope oldEnvelope = null;
277
        if (mapContext != null) {
278
            removeOwnListeners();
279
            if (mapContext.getViewPort() != null) {
280
                oldEnvelope = mapContext.getViewPort().getEnvelope();
281
            }
282
        }
283
        if (viewMapContext == null) { // disconnect the view from the map
284
            this.mapContext = null;
285
            return;
286
        }
287
        try {
288
            if (syncLayers) {
289
                mapContext
290
                        = viewMapContext.createNewFMap(
291
                                (ViewPort) viewMapContext.getViewPort().clone());
292
            } else {
293
                mapContext = viewMapContext.cloneFMap();
294
                mapContext.setViewPort((ViewPort) viewMapContext
295
                        .getViewPort().clone());
296
            }
297
            ViewPort newViewPort = getMapContext().getViewPort();
298
            if (!syncExtents && oldEnvelope != null) {
299
                // if extent is not synced with the view, restore the previous
300
                // envelope if existing
301
                newViewPort.setEnvelope(oldEnvelope);
302
            }
303
            AffineTransform at;
304
            if (getLayoutContext() != null) {
305
                at = getLayoutContext().getAT();
306
            } else {
307
                at = null;
308
            }
309
            newViewPort.setImageSize(new Dimension((int) getBoundingBox(at).width,
310
                    (int) getBoundingBox(at).height));
311
            newViewPort.setDPI(getDrawPaperDPI());
312
            addAllListeners();
313
            updateScaleCtrl();
314
            setTocModel();
315
        } catch (CloneNotSupportedException e1) {
316
            logger.warn("Can't set mapcontext of the fframeview", e1);
317
        }
318

    
319
    }
320

    
321
    /**
322
     * Sets the View associated with this FFrameView, which will be used to
323
     * clone the MapContext and the layers. It will also used to synchronize the
324
     * FFrameView with the associated View, depending on the selected scale type
325
     *
326
     * @param viewDocument
327
     */
328
    public void setView(ViewDocument viewDocument) {
329
        removeViewListeners();
330
        this.viewDocument = viewDocument;
331
        if (this.viewDocument != null) {
332
            setViewMapContext(this.viewDocument.getMapContext());
333
        } else {
334
            setViewMapContext(null);
335
        }
336
    }
337

    
338
    /**
339
     * Gets the associated View
340
     *
341
     * @return The associated view
342
     * @see {@link #setView(ViewDocument)}
343
     */
344
    public ViewDocument getView() {
345
        return viewDocument;
346
    }
347

    
348
    /**
349
     * Draws the FFrameView on the provided Graphics, according to the provided
350
     * affine transform and the visible rectangle.
351
     *
352
     * @param g Graphics2D
353
     * @param at Affine transform to translate sheet coordinates (in cm) to
354
     * screen coordinates (in pixels)
355
     * @param visibleLayoutDocRect visible rectangle
356
     * @param imgBase Image used to speed up the drawing process
357
     */
358
    @Override
359
    public void draw(Graphics2D g, AffineTransform at, Rectangle2D visibleLayoutDocRect, BufferedImage imgBase) {
360
        Rectangle2D.Double fframeViewRect = getBoundingBox(at);
361
        Rectangle2D.Double visibleArea = (Rectangle2D.Double) getVisibleRect(visibleLayoutDocRect, fframeViewRect);
362
        if (visibleArea == null) {
363
            return;
364
        }
365
        preDraw(g, fframeViewRect, visibleArea);
366
        if (getMapContext() == null) {
367
            drawEmpty(g);
368
        } else {
369
            if (FLayoutUtilities.hasEditingLayers(getView())) {
370
                // We are not drawing if any layer is in editing mode
371
                drawMessage(g, Messages.getText(
372
                        "_Cannot_draw_view_if_layers_in_editing_mode"));
373

    
374
            } else {
375
                if (getQuality() == PRESENTATION) {
376
                    try {
377
                        drawPresentation(g, at, fframeViewRect, visibleArea, imgBase);
378
                    } catch (Exception exc) {
379
                        drawMessage(g, FLayoutFunctions.getLastMessage(exc));
380
                    }
381

    
382
                } else {
383
                    drawDraft(g);
384
                }
385
            }
386
        }
387
        postDraw(g, fframeViewRect, at);
388
        if (showGrid && grid != null) {
389
            grid.draw(g, at, visibleLayoutDocRect, imgBase);
390
        }
391
    }
392

    
393
    private void drawMessage(Graphics2D g, String msg) {
394

    
395
        Rectangle2D r = getBoundingBox(null);
396
        g.setColor(Color.lightGray);
397
        g.fillRect((int) r.getX(), (int) r.getY(), (int) r.getWidth(), (int) r.getHeight());
398
        g.setColor(Color.darkGray);
399
        g.setStroke(new BasicStroke(2));
400
        g.drawRect((int) r.getX(), (int) r.getY(), (int) r.getWidth(), (int) r.getHeight());
401
        g.setColor(Color.black);
402

    
403
        int scale = (int) (r.getWidth() / 24);
404
        Font f = new Font("SansSerif", Font.PLAIN, scale);
405
        g.setFont(f);
406
        if (msg == null) {
407
            msg = Messages.getText("error");
408
        }
409
        g.drawString(msg, (int) (r.getCenterX() - ((msg.length() * scale) / 4)),
410
                (int) (r.getCenterY())
411
        );
412
    }
413

    
414
    /**
415
     * Gets the visible envelope, in map coordinates
416
     *
417
     * @param fframeViewRect Rectangle defining the bounding box of the
418
     * FFrameView, in screen coordinates
419
     * @param visiblefframeViewRect Rectangle defining the bounding box of the
420
     * visible area of the fframeView, in screen coordinates
421
     * @return
422
     */
423
    protected Envelope getVisibleEnvelope(Rectangle2D.Double fframeViewRect,
424
            Rectangle2D.Double visiblefframeViewRect) {
425
        Envelope oldEnv = getMapContext().getViewPort().getAdjustedEnvelope();
426
        double widthFactor = ((int) visiblefframeViewRect.width) / fframeViewRect.width;
427
        double heightFactor = ((int) visiblefframeViewRect.height) / fframeViewRect.height;
428

    
429
        double newWidth = oldEnv.getLength(0) * widthFactor;
430
        double newHeight = oldEnv.getLength(1) * heightFactor;
431

    
432
        double translateX = visiblefframeViewRect.x - fframeViewRect.x;
433
        double translateY = visiblefframeViewRect.y - fframeViewRect.y;
434
        double translateFactorX = translateX / fframeViewRect.width;
435
        double translateFactorY = translateY / fframeViewRect.height;
436

    
437
        double newX = oldEnv.getMinimum(0) + translateFactorX * oldEnv.getLength(0);
438
        double newMaxY = oldEnv.getMaximum(1) - translateFactorY * oldEnv.getLength(1);
439
        double newMaxX = newX + newWidth;
440
        double newY = newMaxY - newHeight;
441

    
442
        Envelope newEnv = null;
443
        try {
444
            newEnv = geomManager.createEnvelope(newX, newY, newMaxX, newMaxY, SUBTYPES.GEOM2D);
445
        } catch (CreateEnvelopeException ex) {
446
            logger.warn("Can't calculate the envelope of the visible area.", ex);
447
        }
448
        return newEnv;
449

    
450
    }
451

    
452
    protected void drawPresentation(
453
            Graphics2D g,
454
            AffineTransform affineTransform,
455
            Rectangle2D.Double fframeViewRect,
456
            Rectangle2D.Double visibleRect,
457
            BufferedImage imgBase) throws Exception {
458

    
459
        b_drawing = true;
460
        int drawWidth = (int) visibleRect.width;
461
        int drawHeight = (int) visibleRect.height;
462

    
463
        Envelope oldEnvelope = null;
464
        if (!visibleRect.equals(fframeViewRect)) {
465
            // if visible area is smaller than the fframe, we will only draw this area,
466
            // so we need to tell the ViewPort the image size and extent for drawing,
467
            // and restore the real extent after drawing
468
            oldEnvelope = getMapContext().getViewPort().getEnvelope();
469
            if (oldEnvelope == null) {
470
                return;
471
            }
472

    
473
            Envelope newEnvelope = getVisibleEnvelope(fframeViewRect, visibleRect);
474
            // image size must be set before the envelope, as it has influence on the adjustedExtent
475
            getMapContext().getViewPort().setImageSize(new Dimension(drawWidth, drawHeight));
476
            getMapContext().getViewPort().setEnvelope(newEnvelope);
477
        } else {
478
            getMapContext().getViewPort().setImageSize(new Dimension(drawWidth, drawHeight));
479
            getMapContext().getViewPort().refreshExtent();
480
        }
481

    
482
        // map origin should be calculated using the full fframeview, as the visible position will be relative
483
        Point mapOrigin = new Point((int) fframeViewRect.getMinX(), (int) fframeViewRect.getMaxY());
484

    
485
        // paint the MapContext on m_image, if not already cached
486
        createImage(affineTransform, drawWidth, drawHeight, mapOrigin);
487

    
488
        //Draw the created image
489
        drawImage(g, m_image, visibleRect);
490

    
491
        if (oldEnvelope != null) {
492
            // restore real envelope and image size
493
            getMapContext().getViewPort().setImageSize(new Dimension((int) fframeViewRect.width, (int) fframeViewRect.height));
494
            getMapContext().getViewPort().setEnvelope(oldEnvelope);
495
        }
496

    
497
        scaleAnt = affineTransform.getScaleX();
498
        origin = mapOrigin;
499
        b_drawing = false;
500
    }
501

    
502
    protected void createImage(AffineTransform affineTransform,
503
            int width, int height, Point mapOrigin) throws ReadException, MapContextException {
504
        ViewPort viewPort = this.getMapContext().getViewPort();
505

    
506
        //If the image has to be created...
507
        if (origin == null
508
                || !origin.equals(mapOrigin)
509
                || affineTransform.getScaleX() != scaleAnt
510
                || m_image == null
511
                || !b_validCache) {
512

    
513
            viewPort.setDPI(getDrawPaperDPI());
514
            viewPort.setImageSize(new Dimension(width, height));
515

    
516
            m_image = new BufferedImage(
517
                    width,
518
                    height,
519
                    BufferedImage.TYPE_INT_ARGB
520
            );
521

    
522
            Graphics2D gimg = (Graphics2D) m_image.createGraphics();
523
            getMapContext().draw(m_image, gimg, getScale());
524
            gimg.dispose();
525
            b_validCache = true;
526
        }
527

    
528
    }
529

    
530
    protected void drawImage(Graphics2D g, BufferedImage image,
531
            Rectangle2D.Double visibleRectangle) {
532

    
533
        Color theBackColor = getMapContext().getViewPort().getBackColor();
534
        if (theBackColor != null) {
535
            g.setColor(theBackColor);
536
            g.fillRect((int) visibleRectangle.x, (int) visibleRectangle.y,
537
                    (int) visibleRectangle.width,
538
                    (int) visibleRectangle.height);
539
        }
540
        g.drawImage(m_image,
541
                (int) visibleRectangle.x,
542
                (int) visibleRectangle.y,
543
                null);
544
    }
545

    
546
    protected void preDraw(Graphics2D g, Rectangle2D.Double fframeViewRect, Rectangle2D.Double visibleRect) {
547
        originalGraphicsAT = (AffineTransform) g.getTransform().clone();
548

    
549
        if (g.getClipBounds() != null) {
550
            originalClip = (Rectangle) g.getClipBounds().clone();
551
        }
552
        AffineTransform rotationAT = getRotationAT();
553
        if (rotationAT != null) {
554
            g.transform(rotationAT);
555
        }
556
        g.setClip((int) visibleRect.getMinX(), (int) visibleRect.getMinY(),
557
                (int) visibleRect.getWidth(), (int) visibleRect.getHeight());
558
    }
559

    
560
    protected void postDraw(Graphics2D g, Rectangle2D.Double fframeViewRect, AffineTransform at) {
561
        g.setTransform(originalGraphicsAT);
562
        if (getMapContext() != null) {
563
            setATMap(getMapContext().getViewPort().getAffineTransform());
564
        }
565
        if (originalClip != null) {
566
            g.setClip(originalClip.x, originalClip.y, originalClip.width, originalClip.height);
567
        }
568
    }
569

    
570
    /**
571
     * @param g
572
     * @param rectangleLayout
573
     * @param rectangleView
574
     * @param originalClip
575
     * @param imgBase
576
     * @param at
577
     * @deprecated Use
578
     * {@link #postDraw(Graphics2D, java.awt.geom.Rectangle2D.Double, AffineTransform)}
579
     * instead.
580
     */
581
    @Deprecated
582
    protected void postDraw(Graphics2D g, Rectangle2D.Double rectangleLayout, Rectangle2D rectangleView, BufferedImage imgBase, Rectangle originalClip, AffineTransform at) {
583
        postDraw(g, rectangleLayout, at);
584

    
585
    }
586

    
587
    /**
588
     * @param g
589
     * @param rectangleLayout
590
     * @return
591
     * @deprecated Use {@link #preDraw(Graphics2D, java.awt.geom.Rectangle2D.Double, java.awt.geom.Rectangle2D.Double) instead
592
     */
593
    @Deprecated
594
    protected Rectangle preDraw(Graphics2D g, Rectangle2D.Double rectangleLayout) {
595
        Rectangle originalClip = null;
596
        if (g.getClipBounds() != null) {
597
            originalClip = (Rectangle) g.getClipBounds().clone();
598
        }
599
        preDraw(g, rectangleLayout, rectangleLayout);
600
        return originalClip;
601
    }
602

    
603
    @Override
604
    public void print(Graphics2D g, AffineTransform at, Geometry geom,
605
            PrintAttributes printAttributes) {
606
        Rectangle2D.Double rectangleLayout = getBoundingBox(at);
607

    
608
        preDraw(g, rectangleLayout, rectangleLayout);
609
        print(g, at, printAttributes);
610
        postDraw(g, rectangleLayout, at);
611
        if (showGrid && grid != null) {
612
            grid.print(g, at, geom, printAttributes);
613
        }
614
    }
615

    
616
    protected void print(Graphics2D g, AffineTransform at, PrintAttributes printAttributes) {
617
        Rectangle2D.Double layoutRectangle = getBoundingBox(at);
618

    
619
        // FIXME: should we clone the mapcontext and viewport before printing ??
620
        // otherwise we will probably have unexpected results if the user modifies
621
        // the layout while printing (answer: not an issue at the moment as printing is
622
        // a blocking operation)
623
        ViewPort viewPort = this.getMapContext().getViewPort();
624

    
625
        Point2D old_offset = viewPort.getOffset();
626
        Dimension old_imgsize = viewPort.getImageSize();
627
        double oldDpi = viewPort.getDPI();
628

    
629
        viewPort.setOffset(new Point2D.Double(layoutRectangle.x, layoutRectangle.y));
630
        viewPort.setImageSize(
631
                new Dimension(
632
                        (int) layoutRectangle.width,
633
                        (int) layoutRectangle.height
634
                )
635
        );
636
        double dpi = PrintAttributes.PRINT_QUALITY_DPI[printAttributes.getPrintQuality()];
637
        viewPort.setDPI(dpi);
638

    
639
        //Draw the backgroung color of the map
640
        Color theBackColor = viewPort.getBackColor();
641
        if (theBackColor != null) {
642
            g.setColor(theBackColor);
643
            g.fillRect(
644
                    (int) layoutRectangle.x,
645
                    (int) layoutRectangle.y,
646
                    viewPort.getImageWidth(),
647
                    viewPort.getImageHeight()
648
            );
649
        }
650

    
651
        //Print the map
652
        try {
653
            this.getMapContext().print(g, getScale(), printAttributes);
654
        } catch (Exception ex) {
655
            logger.warn("Can't print frame view.", ex);
656
        }
657

    
658
        // Restore offset, imgsize
659
        viewPort.setOffset(old_offset);
660
        viewPort.setImageSize(old_imgsize);
661
        viewPort.setDPI(oldDpi);
662

    
663
    }
664

    
665
    /**
666
     * Rellena la unidad de medida en la que est? la vista.
667
     *
668
     * @param i entero que representa la unidad de medida de la vista.
669
     */
670
    public void setMapUnits(int i) {
671
        mapUnits = i;
672
    }
673

    
674
    /**
675
     * Obtiene la unidad de medida en la que est? la vista.
676
     *
677
     * @return Unidad de medida.
678
     */
679
    public int getMapUnits() {
680
        return mapUnits;
681
    }
682

    
683
    /**
684
     * Devuelve la escala seg?n el tipo de escala que se haya seleccionado al
685
     * a?adida la vista.
686
     *
687
     * @return escala.
688
     */
689
    public long getScale() {
690
        return (long) getMapContext().getScaleView();
691
    }
692

    
693
    /**
694
     * Inserta la imagen para repintar el FFrameView.
695
     *
696
     * @param bi Imagen para repintar.
697
     */
698
    public void setBufferedImage(BufferedImage bi) {
699
        m_image = bi;
700
    }
701

    
702
    /**
703
     * Devuelve la imagen para repintar.
704
     *
705
     * @return Imagen para repintar.
706
     */
707
    @Override
708
    public BufferedImage getBufferedImage() {
709
        return m_image;
710
    }
711

    
712
    /**
713
     * Devuelve la MAtriz de transformaci?n utilizada por la FFrameView.
714
     *
715
     * @return MAtriz de transformaci?n.
716
     */
717
    @Override
718
    public AffineTransform getATMap() {
719
        return mapAT;
720
    }
721

    
722
    /**
723
     * Inserta la matriz de transformaci?n.
724
     *
725
     * @param transform Matriz de transformaci?n.
726
     */
727
    @Override
728
    public void setATMap(AffineTransform transform) {
729
        mapAT = transform;
730
    }
731

    
732
    /**
733
     * Inserta el proyecto.
734
     *
735
     * @param p Proyecto.
736
     */
737
    @Override
738
    public void setProject(Project p) {
739
        project = p;
740
    }
741

    
742
    /**
743
     * @see
744
     * org.gvsig.app.project.documents.layout.fframes.IFFrame#getNameFFrame()
745
     */
746
    @Override
747
    public String getNameFFrame() {
748
        return PluginServices.getText(this, "Vista") + num;
749
    }
750

    
751
    @Override
752
    public String getName() {
753
        return PERSISTENCE_DEFINITION_NAME;
754
    }
755

    
756
    public boolean compare(Object arg0) {
757
        if (!(arg0 instanceof FFrameView)) {
758
            return false;
759
        }
760

    
761
        if (!this.getName().equals(((FFrameView) arg0).getName())) {
762
            return false;
763
        }
764

    
765
        if (Math.abs(this.getBoundBox().getWidth()
766
                - (((FFrameView) arg0).getBoundBox().getWidth())) > 0.05) {
767
            return false;
768
        }
769
        if (Math.abs(this.getBoundBox().getHeight()
770
                - (((FFrameView) arg0).getBoundBox().getHeight())) > 0.05) {
771
            return false;
772
        }
773

    
774
        if (!this.toString().equals(((FFrameView) arg0).toString())) {
775
            return false;
776
        }
777

    
778
        if (this.getMapContext() != null
779
                && !this.getMapContext()
780
                        .equals(((FFrameView) arg0).getMapContext())) {
781
            return false;
782
        }
783

    
784
        if (this.getRotation() != ((FFrameView) arg0).getRotation()) {
785
            return false;
786
        }
787
        return true;
788
    }
789

    
790
    public void updateScaleCtrl() {
791
        NewStatusBar statusbar = PluginServices.getMainFrame().getStatusBar();
792
        MapContext mapContext = this.getMapContext();
793
        if (mapContext == null) {
794
            return;
795
        }
796
        statusbar.setMessage("units",
797
                PluginServices.getText(this, mapContext.getDistanceName())
798
        );
799
        String scale;
800
        if (fixedScale != null && getScaleType() == SCALE_TYPE.FIXED_SCALE) {
801
            // prefer fixedScale as getScaleView() may offer slight differences
802
            // because the influence of the adjusted envelope
803
            scale = String.valueOf(fixedScale.longValue());
804
        } else {
805
            scale = String.valueOf(getMapContext().getScaleView());
806
        }
807
        statusbar.setControlValue("layout-view-change-scale",
808
                scale);
809
        IProjection proj = mapContext.getViewPort().getProjection();
810
        if (proj != null) {
811
            statusbar.setMessage("projection", proj.getAbrev());
812
        } else {
813
            statusbar.setMessage("projection", "");
814
        }
815
    }
816

    
817
    @Override
818
    public void fullExtent() throws ReadException {
819
        setNewEnvelope(getMapContext().getFullEnvelope());
820
    }
821

    
822
    @Override
823
    public void setPointsToZoom(Point2D px1, Point2D px2) {
824
        p1 = px1;
825
        p2 = px2;
826
    }
827

    
828
    @Override
829
    public void movePoints(Point2D px1, Point2D px2) {
830
        double difX = -px2.getX() + px1.getX();
831
        double difY = -px2.getY() + px1.getY();
832
        if (p1 != null) {
833
            p1.setLocation(p1.getX() + difX, p1.getY() + difY);
834
            p2.setLocation(p2.getX() + difX, p2.getY() + difY);
835
        }
836
    }
837

    
838
    @Override
839
    public FFrameView clone() throws CloneNotSupportedException {
840
//        logger.info("clone FFrameView "+ Integer.toHexString(this.hashCode()));
841
        FFrameView frame = (FFrameView) super.clone();
842
        frame.createListeners(); // necessary to create the listeners within the right scope
843
        frame.setView(this.getView());
844
        frame.removeAllListeners(); // remove listeners add by setView
845

    
846
        if (grid != null) {
847
            FFrameGrid newGrid = (FFrameGrid) this.grid.clone();
848
            newGrid.setFFrameDependence(frame);
849
            frame.setGrid(newGrid);
850
        }
851
//        logger.info("cloned FFrameView "+ Integer.toHexString(frame.hashCode()));
852
        return frame;
853
    }
854

    
855
    public void setGrid(IFFrame grid) {
856
        this.grid = grid;
857
        this.grid.setRotation(this.getRotation());
858
    }
859

    
860
    public IFFrame getGrid() {
861
        return this.grid;
862
    }
863

    
864
    @Override
865
    public void setRotation(double rotation) {
866
        super.setRotation(rotation);
867
        if (grid != null) {
868
            grid.setRotation(rotation);
869
        }
870
    }
871

    
872
    public void showGrid(boolean b) {
873
        showGrid = b;
874
    }
875

    
876
    public boolean isShowGrid() {
877
        return showGrid;
878
    }
879

    
880
    @Override
881
    public void refreshOriginalExtent() {
882
    }
883

    
884
    public static void registerPersistent() {
885
        PersistenceManager manager = ToolsLocator.getPersistenceManager();
886
        if (manager.getDefinition(PERSISTENCE_DEFINITION_NAME) == null) {
887
            DynStruct definition = manager.addDefinition(
888
                    FFrameView.class,
889
                    PERSISTENCE_DEFINITION_NAME,
890
                    "FFrameView persistence definition", null, null
891
            );
892
            definition.extend(
893
                    manager.getDefinition(FFrame.PERSISTENCE_DEFINITION_NAME)
894
            );
895
            definition.addDynFieldInt(QUALITY_FIELD).setMandatory(true);
896
            definition.addDynFieldInt(MAPUNITS_FIELD).setMandatory(true);
897
            definition.addDynFieldDouble(SCALE_FIELD).setMandatory(false);
898
            definition.addDynFieldObject(VIEW_FIELD)
899
                    .setClassOfValue(ViewDocument.class).setMandatory(false);
900
            definition.addDynFieldObject(ENVELOPE_FIELD)
901
                    .setClassOfValue(Envelope.class).setMandatory(false);
902
            definition.addDynFieldBoolean(SHOWGRID_FIELD).setMandatory(true);
903
            definition.addDynFieldObject(GRID_FIELD)
904
                    .setClassOfValue(IFFrame.class).setMandatory(false);
905
            definition.addDynFieldBoolean(HAS_TOC_FIELD).setMandatory(false);
906
            definition.addDynFieldBoolean(EXTENT_SYNC_FIELD).setMandatory(false);
907
            definition.addDynFieldBoolean(LAYER_SYNC_FIELD).setMandatory(false);
908
            definition.addDynFieldInt(SCALE_TYPE_FIELD).setMandatory(false);
909
            // unused fields, kept for backward compatibility
910
            definition.addDynFieldInt(MODE_FIELD).setMandatory(false);
911
            definition.addDynFieldInt(TYPESCALE_FIELD).setMandatory(false);
912
            definition.addDynFieldBoolean(BLINKED_FIELD).setMandatory(false);
913
            definition.addDynFieldObject(MAPCONTEXT_FIELD)
914
                    .setClassOfValue(MapContext.class).setMandatory(false);
915
            definition.addDynFieldInt(VIEWING_FIELD).setMandatory(false);
916
            definition.addDynFieldInt(EXTENSION_FIELD).setMandatory(false);
917
        }
918
    }
919

    
920
    @Override
921
    public void loadFromState(PersistentState state)
922
            throws PersistenceException {
923
        super.loadFromState(state);
924
        b_frameInitialized = false;
925
        if (state.hasValue(EXTENT_SYNC_FIELD)) {
926
            syncExtents = state.getBoolean(EXTENT_SYNC_FIELD);
927
        } else {
928
            syncExtents = true;
929
        }
930
        if (state.hasValue(LAYER_SYNC_FIELD)) {
931
            syncLayers = state.getBoolean(LAYER_SYNC_FIELD);
932
        } else {
933
            syncLayers = true;
934
        }
935
        Double layoutScale = null;
936
        if (state.hasValue(SCALE_FIELD)) {
937
            layoutScale = state.getDouble(SCALE_FIELD);
938
        }
939
        Envelope envelope = (Envelope) state.get(ENVELOPE_FIELD);
940

    
941
        if (state.hasValue(SCALE_TYPE_FIELD)) {
942
            int value = state.getInt(SCALE_TYPE_FIELD);
943
            if (value == SCALE_TYPE.FIXED_EXTENT.ordinal()) {
944
                scaleType = SCALE_TYPE.FIXED_EXTENT;
945
                fixedExtent = envelope;
946
            } else if (value == SCALE_TYPE.FIXED_SCALE.ordinal()) {
947
                scaleType = SCALE_TYPE.FIXED_SCALE;
948
                fixedScale = layoutScale;
949
            }
950
            // else use the default value
951
        }
952
        quality = state.getInt(QUALITY_FIELD);
953
        mapUnits = state.getInt(MAPUNITS_FIELD);
954
        if (state.hasValue(HAS_TOC_FIELD)) {
955
            this.b_hasToc = state.getBoolean(HAS_TOC_FIELD);
956
        }
957

    
958
        // When copying and pasting layout documents (in project manager), the view
959
        // is first persisted (copy action) and then re-created from persistence (paste action).
960
        // This is a problem, as the re-created view is not the same view as the original one
961
        // and thus layout stops being synchronized with the right view.
962
        // Therefore, we try to get first the "live" view if available, and get the persisted
963
        // one otherwise, which will be used when opening projects.
964
        ViewDocument persistenceView = (ViewDocument) state.get(VIEW_FIELD);
965
        if (persistenceView != null) {
966
            ViewDocument view = (ViewDocument) ProjectManager.getInstance().getCurrentProject().getDocument(persistenceView.getName(), persistenceView.getTypeName());
967
            if (view == null) {
968
                view = persistenceView;
969
            }
970
            this.setView(view);
971
            // it is crucial to don't persist the MapContext and get a cloned one from the View instead,
972
            // as the cloned instance is different from the one created using persistence. In particular,
973
            // the cloned one will share the EventBuffer with the original one, while persistence would
974
            // create 2 separate EventBuffers, which will then have a very stange behaviour
975
        }
976
        if (getMapContext() != null) {
977
            if (layoutScale != null) {
978
                getMapContext().setScaleView(layoutScale.longValue());
979
            }
980
            getMapContext().getViewPort().setEnvelope(envelope);
981
            if (this.getLayoutContext() != null) {
982
                this.getLayoutContext().setTocModel(getMapContext());
983
            }
984
        }
985
        showGrid = state.getBoolean(SHOWGRID_FIELD);
986
        grid = (IFFrame) state.get(GRID_FIELD);
987
    }
988

    
989
    @Override
990
    public void saveToState(PersistentState state) throws PersistenceException {
991
        super.saveToState(state);
992
        state.set(EXTENT_SYNC_FIELD, syncExtents);
993
        state.set(LAYER_SYNC_FIELD, syncLayers);
994
        state.set(QUALITY_FIELD, quality);
995
        state.set(MAPUNITS_FIELD, mapUnits);
996
        state.set(VIEW_FIELD, viewDocument);
997
        state.set(HAS_TOC_FIELD, b_hasToc);
998
        state.set(SCALE_TYPE_FIELD, scaleType.ordinal());
999

    
1000
        if (getMapContext() != null
1001
                && getMapContext().getViewPort().getEnvelope() != null) {
1002
            if (scaleType == SCALE_TYPE.FIXED_SCALE) {
1003
                if (fixedScale == null) {
1004
                    fixedScale = new Double(getMapContext().getScaleView());
1005
                }
1006
                state.set(SCALE_FIELD, (double) fixedScale);
1007
            } else {
1008
                state.set(SCALE_FIELD, (double) getMapContext().getScaleView());
1009
            }
1010
            if (scaleType == SCALE_TYPE.FIXED_EXTENT) {
1011
                if (fixedExtent == null) {
1012
                    fixedExtent = getMapContext().getViewPort().getAdjustedEnvelope();
1013
                }
1014
                state.set(ENVELOPE_FIELD, fixedExtent);
1015
            } else {
1016
                state.set(ENVELOPE_FIELD, getMapContext().getViewPort()
1017
                        .getAdjustedEnvelope());
1018
            }
1019
        }
1020
        state.set(SHOWGRID_FIELD, showGrid);
1021
        state.set(GRID_FIELD, grid);
1022
    }
1023

    
1024
    @Override
1025
    public void setBoundBox(Rectangle2D r) {
1026
        super.setBoundBox(r);
1027
        if (this.getMapContext() != null && this.getLayoutContext() != null) {
1028
            AffineTransform at = this.getLayoutContext().getAT();
1029
            long scale = getMapContext().getScaleView();
1030
            getMapContext().getViewPort().setImageSize(
1031
                    new Dimension(
1032
                            (int) getBoundingBox(at).getWidth(),
1033
                            (int) getBoundingBox(at).getHeight()
1034
                    )
1035
            );
1036
            getMapContext().getViewPort().setDPI(getDrawPaperDPI());
1037
            if (getScaleType() == SCALE_TYPE.FIXED_SCALE) {
1038
                getMapContext().setScaleView((long) scale);
1039
            }
1040
            updateScaleCtrl();
1041
            refresh();
1042
        }
1043
    }
1044

    
1045
    /**
1046
     * Gets the rotation of the frame
1047
     *
1048
     * @return Rotation in degrees
1049
     */
1050
    public double getMapRotation() {
1051
        return 0;
1052
    }
1053

    
1054
    protected void invalidateLayout() {
1055
        b_validCache = false;
1056
        if (getLayoutContext() != null) {
1057
            getLayoutContext().notifAllObservers();
1058
        }
1059
        observers.notifyObservers(this,
1060
                new DefaultLayoutNotification(LayoutNotification.LAYOUT_REFRESH)
1061
        );
1062
    }
1063

    
1064
    protected void invalidateToc() {
1065
        if (getLayoutContext() != null) {
1066
            getLayoutContext().notifyTocUpdated(TocModelChangedNotification.Type.ITEM_UPDATED);
1067
        }
1068
    }
1069

    
1070
    protected void refreshToc() {
1071
        if (getLayoutContext() != null) {
1072
            getLayoutContext().notifyTocUpdated(TocModelChangedNotification.Type.MODEL_CHANGED);
1073
        }
1074
    }
1075

    
1076
    @Override
1077
    public void refresh() {
1078
        // force re-creating the cache on the next drawing cycle
1079
        b_validCache = false;
1080
    }
1081

    
1082
    protected void createListeners() {
1083
        this.removeAllListeners();
1084
        viewDocListener = new ViewDocListener(this);
1085
        ownMapContextListener = new OwnMapContextListener(this);
1086
//        logger.info("createListeners.viewDocListener "+ viewDocListener.getID());
1087
//        logger.info("createListeners.ownMapContextListener "+ ownMapContextListener.getID());
1088
    }
1089

    
1090
    protected void addAllListeners() {
1091
        if (viewDocListener == null && ownMapContextListener == null) {
1092
            this.createListeners();
1093
        }
1094
        if (getView() != null) {
1095
            if (syncLayers) {
1096
                getView().getMapContext().addLayerListener(viewDocListener);
1097
                getView().getMapContext().getLayers().addLayerCollectionListener(viewDocListener);
1098
                getView().getMapContext().addAtomicEventListener(viewDocListener);
1099
            }
1100
            if (getExtentSynced()) {
1101
                getView().getMapContext().getViewPort().addViewPortListener(viewDocListener);
1102
            }
1103
        }
1104
        if (getMapContext() != null) {
1105
            getMapContext().addLayerListener(ownMapContextListener);
1106
            getMapContext().getLayers().addLayerCollectionListener(ownMapContextListener);
1107
            getMapContext().getViewPort().addViewPortListener(ownMapContextListener);
1108
        }
1109
//        logger.info("addAllListeners.viewDocListener " + viewDocListener.getID());
1110
//        logger.info("addAllListeners.ownMapContextListener " + ownMapContextListener.getID());
1111

    
1112
    }
1113

    
1114
    protected void resetAllListeners() {
1115
        this.removeAllListeners();
1116
        this.addAllListeners();
1117
    }
1118

    
1119
    protected void removeAllListeners() {
1120
        removeOwnListeners();
1121
        removeViewListeners();
1122
    }
1123

    
1124
    protected void removeOwnListeners() {
1125
        if (this.ownMapContextListener == null) {
1126
            return;
1127
        }
1128
        if (this.mapContext == null) {
1129
            return;
1130
        }
1131
        this.mapContext.removeLayerListener(ownMapContextListener);
1132
        this.mapContext.getViewPort().removeViewPortListener(ownMapContextListener);
1133
        this.mapContext.getLayers().removeLayerCollectionListener(ownMapContextListener);
1134
//        logger.info("removeOwnListeners.ownMapContextListener " + ownMapContextListener.getID());
1135
    }
1136

    
1137
    protected void removeViewListeners() {
1138
        if (this.viewDocListener == null) {
1139
            return;
1140
        }
1141
        if (this.getView() == null) {
1142
            return;
1143
        }
1144
        MapContext mapContext = this.getView().getMapContext();
1145
        if (mapContext == null) {
1146
            return;
1147
        }
1148
        mapContext.removeLayerListener(viewDocListener);
1149
        mapContext.getViewPort().removeViewPortListener(viewDocListener);
1150
        mapContext.getLayers().removeLayerCollectionListener(viewDocListener);
1151
        mapContext.removeAtomicEventListener(viewDocListener);
1152
//        logger.info("removeViewListeners.viewDocListener " + viewDocListener.getID());
1153
    }
1154

    
1155
    @Override
1156
    public void dispose() {
1157
        this.removeAllListeners();
1158
        this.viewDocListener = null;
1159
        this.ownMapContextListener = null;
1160
        this.viewDocument = null;
1161
        this.mapContext = null;
1162
    }
1163

    
1164
    @Override
1165
    public void frameRemoved() {
1166
        this.removeAllListeners();
1167
        if (b_hasToc && getLayoutContext() != null) {
1168
            getLayoutContext().setTocModel(null);
1169
        }
1170
        m_image = null; // FIXME: we could instead move it to a LRU cache to keep the last N images
1171
    }
1172

    
1173
    @Override
1174
    public void frameAdded() {
1175
        addAllListeners();
1176
        setTocModel();
1177
        updateScaleCtrl();
1178
    }
1179

    
1180
    public void setHasToc(boolean hasToc) {
1181
        this.b_hasToc = hasToc;
1182
        setTocModel();
1183
    }
1184

    
1185
    protected void setTocModel() {
1186
        if (getLayoutContext() != null) {
1187
            if (b_hasToc && getMapContext() != null) {
1188
                getLayoutContext().setTocModel(getMapContext());
1189
            } else {
1190
                getLayoutContext().setTocModel(null);
1191
            }
1192
        }
1193
    }
1194

    
1195
    @Override
1196
    protected void doSetSelected(int selectedStatus) {
1197
        boolean oldSelectedStatus = isSelected();
1198
        super.doSetSelected(selectedStatus);
1199
        if (!oldSelectedStatus && isSelected()) { // changed from not selected to selected
1200
            setTocModel();
1201
            updateScaleCtrl();
1202
        }
1203
    }
1204

    
1205
    @Override
1206
    public boolean getLayerSynced() {
1207
        return syncLayers;
1208
    }
1209

    
1210
    @Override
1211
    public void setLayerSynced(boolean synced) {
1212
        syncLayers = synced;
1213
        resetAllListeners();
1214
    }
1215

    
1216
    @Override
1217
    public boolean getExtentSynced() {
1218
        return syncExtents;
1219
    }
1220

    
1221
    @Override
1222
    public void setExtentSynced(boolean synced) {
1223
        syncExtents = synced;
1224
        resetAllListeners();
1225
    }
1226

    
1227
    /**
1228
     * Returns true if the newEnvelope represents a pan operation on
1229
     * oldEnvelope, (both extents have the same height and width)
1230
     *
1231
     * @param oldEnvelope
1232
     * @param newEnvelope
1233
     * @return
1234
     */
1235
    public static boolean isPan(Envelope oldEnvelope, Envelope newEnvelope) {
1236
        if (oldEnvelope != null && newEnvelope != null) {
1237
            double toleranceX = 0.00000001 * Math.min(
1238
                    oldEnvelope.getLength(0), newEnvelope.getLength(0)
1239
            );
1240
            double toleranceY = 0.00000001 * Math.min(
1241
                    oldEnvelope.getLength(1), newEnvelope.getLength(1)
1242
            );
1243

    
1244
            // we consider it to be a pan if both lengths are equal
1245
            // (we use tolerance to avoid direct comparison of double values)
1246
            if (((oldEnvelope.getLength(0) - newEnvelope.getLength(0)) < toleranceX)
1247
                    && ((oldEnvelope.getLength(1) - newEnvelope.getLength(1)) < toleranceY)) {
1248
                return true;
1249
            }
1250
        }
1251
        return false;
1252
    }
1253

    
1254
    /**
1255
     * Calculates the new extent for the FFrame. It is necessary to avoid scale
1256
     * changes when a pan is triggered from the view, as the different size
1257
     * factors from View/FFrameView introduces scale changes even for pans
1258
     *
1259
     * @return
1260
     */
1261
    protected Envelope calculateNewExtent() {
1262
        Envelope newEnvelope = getView().getMapContext().getViewPort().getEnvelope();
1263
        Envelope oldViewEnvelope = null;
1264
        try {
1265
            if (getView().getMapContext().getViewPort().getEnvelopes().hasPrevious()) {
1266
                Rectangle2D r = getView().getMapContext().getViewPort().getEnvelopes().getPrev();
1267
                oldViewEnvelope = GeometryLocator.getGeometryManager().createEnvelope(
1268
                        r.getMinX(), r.getMinY(), r.getMaxX(), r.getMaxY(), SUBTYPES.GEOM2D
1269
                );
1270
            }
1271
            if (isPan(oldViewEnvelope, newEnvelope)) {
1272
                Envelope envelope = getMapContext().getViewPort().getAdjustedEnvelope();
1273
                double shiftX = newEnvelope.getMinimum(0) - oldViewEnvelope.getMinimum(0);
1274
                double shiftY = newEnvelope.getMinimum(1) - oldViewEnvelope.getMinimum(1);
1275

    
1276
                double minX = envelope.getMinimum(0) + shiftX;
1277
                double minY = envelope.getMinimum(1) + shiftY;
1278
                double maxX = envelope.getMaximum(0) + shiftX;
1279
                double maxY = envelope.getMaximum(1) + shiftY;
1280

    
1281
                return GeometryLocator.getGeometryManager().createEnvelope(
1282
                        minX, minY, maxX, maxY, SUBTYPES.GEOM2D
1283
                );
1284
            }
1285
        } catch (Exception ex) {
1286
        }
1287
        return newEnvelope;
1288
    }
1289

    
1290
    protected boolean isDrawing() {
1291
        return this.b_drawing;
1292
    }
1293

    
1294
    protected boolean isUpdating() {
1295
        return this.b_updating;
1296
    }
1297

    
1298
    protected boolean skipFirstChangeExtent() {
1299
        if (this.b_frameInitialized) {
1300
            return false;
1301
        }
1302
        this.b_frameInitialized = true;
1303
        return true;
1304
    }
1305

    
1306
    protected boolean isSyncLayers() {
1307
        return this.syncLayers;
1308
    }
1309

    
1310
    protected void beginUpdate() {
1311
        this.b_updating = true;
1312
    }
1313

    
1314
    protected void endUpdate() {
1315
        this.b_updating = false;
1316
    }
1317

    
1318
    @Override
1319
    public SCALE_TYPE getScaleType() {
1320
        return this.scaleType;
1321
    }
1322

    
1323
    public Double getFixedScale() {
1324
        return this.fixedScale;
1325
    }
1326

    
1327
    public Envelope getFixedExtent() {
1328
        return this.fixedExtent;
1329
    }
1330

    
1331
    public void setExtent(Envelope extent) {
1332
        if (getScaleType() == SCALE_TYPE.NORMAL) {
1333
            getMapContext().getViewPort().setEnvelope(extent);
1334
        }
1335
    }
1336

    
1337
    @Override
1338
    public void setScaleType(SCALE_TYPE scaleType) {
1339
        this.scaleType = scaleType;
1340
    }
1341

    
1342
    public void setScaleType(SCALE_TYPE scaleType, double fixedScale) {
1343
        if (scaleType == SCALE_TYPE.FIXED_SCALE) {
1344
            this.scaleType = scaleType;
1345
            this.fixedScale = fixedScale;
1346
            setScale(fixedScale);
1347
        }
1348
    }
1349

    
1350
    public void setScaleType(SCALE_TYPE scaleType, Envelope fixedExtent) {
1351
        if (scaleType == SCALE_TYPE.FIXED_EXTENT) {
1352
            this.scaleType = scaleType;
1353
            this.fixedExtent = fixedExtent;
1354
            setNewEnvelope(fixedExtent);
1355
        }
1356
    }
1357

    
1358
    @Deprecated
1359
    @Override
1360
    public int getTypeScale() {
1361
        return AUTOMATICO;
1362
    }
1363

    
1364
    @Deprecated
1365
    @Override
1366
    public void setLinked(boolean b) {
1367
    }
1368

    
1369
    @Deprecated
1370
    @Override
1371
    public boolean getLinked() {
1372
        return false;
1373
    }
1374

    
1375
    @Override
1376
    public void windowActivated() {
1377
        if (isSelected()
1378
                && getLayoutContext().getSelectedFFrames(IFFrameUseFMap.class).length == 1) {
1379
            // update the scale control whenever the panel window gets activated,
1380
            // as the control instance is shared for all the windows
1381
            updateScaleCtrl();
1382
        }
1383

    
1384
    }
1385

    
1386
    @Override
1387
    public void windowClosed() {
1388
    }
1389
    
1390
    
1391
    private class ViewDocListener
1392
            implements ViewPortListener, LegendListener, LayerCollectionListener, AtomicEventListener {
1393

    
1394
        public ViewDocListener(FFrameView fview) {
1395

    
1396
        }
1397

    
1398
        @Override
1399
        public void extentChanged(ExtentEvent e) {
1400
            if (isUpdating()) {
1401
                return;
1402
            }
1403
            if (skipFirstChangeExtent()) {
1404
                return;
1405
            }
1406
            if (!getExtentSynced()) {
1407
                return;
1408
            }
1409
//                logger.info("viewdoc-mapcontext.extentChanged " + this.getID());
1410
            try {
1411
                beginUpdate();
1412
                if (getMapContext() != null) {
1413
                    if (null == getScaleType()) {
1414
                        getMapContext().getViewPort().setEnvelope(calculateNewExtent());
1415
                    } else {
1416
                        switch (getScaleType()) {
1417
                            case FIXED_EXTENT:
1418
                                getView().getMapContext().getViewPort().setEnvelope(getFixedExtent());
1419
                                break;
1420
                            case FIXED_SCALE:
1421
                                getMapContext().setScaleView(Math.round(getFixedScale()));
1422
                                getView().getMapContext().getViewPort().setEnvelope(getMapContext().getViewPort().getAdjustedEnvelope());
1423
                                break;
1424
                            default:
1425
                                getMapContext().getViewPort().setEnvelope(calculateNewExtent());
1426
                                break;
1427
                        }
1428
                    }
1429
                    updateScaleCtrl();
1430
                    invalidateLayout();
1431
                }
1432
            } finally {
1433
                endUpdate();
1434
            }
1435
        }
1436

    
1437
        @Override
1438
        public void backColorChanged(ColorEvent e) {
1439
            if (!isUpdating() && isSyncLayers()) {
1440
                if (getMapContext() != null) {
1441
                    beginUpdate();
1442
                    getMapContext().getViewPort().setBackColor(e.getNewColor());
1443
                    invalidateLayout();
1444
                    endUpdate();
1445
                }
1446
            }
1447
        }
1448

    
1449
        @Override
1450
        public void projectionChanged(ProjectionEvent e) {
1451
            if (!isUpdating() && getExtentSynced()) {
1452
                if (getMapContext() != null) {
1453
                    beginUpdate();
1454
                    getMapContext().getViewPort().setProjection(e.getNewProjection());
1455
                    invalidateLayout();
1456
                    // FIXME: force also a view redraw someway??
1457
                    endUpdate();
1458
                }
1459
            }
1460
        }
1461

    
1462
        public void conditionalRedraw() {
1463
            if (!isUpdating() && isSyncLayers()) {
1464
                beginUpdate();
1465
                invalidateLayout();
1466
                // the view should also receive the event and update automatically
1467
                endUpdate();
1468
            }
1469
        }
1470

    
1471
        @Override
1472
        public void legendChanged(LegendChangedEvent e) {
1473
            conditionalRedraw();
1474
            refreshToc();
1475
        }
1476

    
1477
        @Override
1478
        public void layerAdded(LayerCollectionEvent e) {
1479
            conditionalRedraw();
1480
            // no need to invalidate the toc as it is down by the OwnMapContextListener
1481
        }
1482

    
1483
        @Override
1484
        public void layerMoved(LayerPositionEvent e) {
1485
            conditionalRedraw();
1486
            // no need to invalidate the toc as it is down by the OwnMapContextListener
1487
        }
1488

    
1489
        @Override
1490
        public void layerRemoved(LayerCollectionEvent e) {
1491
            conditionalRedraw();
1492
            // no need to invalidate the toc as it is down by the OwnMapContextListener
1493
        }
1494

    
1495
        @Override
1496
        public void layerAdding(LayerCollectionEvent e)
1497
                throws CancelationException {
1498
            // nothing needed
1499
        }
1500

    
1501
        @Override
1502
        public void layerMoving(LayerPositionEvent e)
1503
                throws CancelationException {
1504
            // nothing needed
1505
        }
1506

    
1507
        @Override
1508
        public void layerRemoving(LayerCollectionEvent e)
1509
                throws CancelationException {
1510
            // nothing needed
1511
        }
1512

    
1513
        @Override
1514
        public void visibilityChanged(LayerCollectionEvent e)
1515
                throws CancelationException {
1516
            // AtomicEvent is catching visibility changes instead of this one,
1517
            // as this event is not being received. Maybe is only triggered if the
1518
            // visibility of the whole group changes??
1519
            conditionalRedraw();
1520
            invalidateToc();
1521
        }
1522

    
1523
        @Override
1524
        public void atomicEvent(AtomicEvent e) {
1525
            boolean layoutRedraw = false;
1526
            // not all the events require a fframeview redraw
1527
            for (int i = e.getLayerEvents().length - 1; i >= 0; i--) {
1528
                FMapEvent at = e.getEvent(i);
1529
                if (at instanceof LayerEvent) {
1530
                    if (at.getEventType() == LayerEvent.DRAW_VALUES_CHANGED) {
1531
                        layoutRedraw = true;
1532
                        break;
1533
                    }
1534
                }
1535
            }
1536
            if (layoutRedraw) {
1537
                conditionalRedraw();
1538
            }
1539
            invalidateToc();
1540
        }
1541
    }
1542

    
1543
    private class OwnMapContextListener
1544
            implements ViewPortListener, LegendListener, LayerCollectionListener {
1545

    
1546
        public OwnMapContextListener(FFrameView fview) {
1547

    
1548
        }
1549

    
1550
        @Override
1551
        public void extentChanged(ExtentEvent e) {
1552
            if (isDrawing()) {
1553
                return;
1554
            }
1555
            if (isUpdating()) {
1556
                return;
1557
            }
1558
            try {
1559
//                logger.info("layout-mapcontext.extentChanged "+this.getID());
1560
                beginUpdate();
1561
                if (getScaleType() == SCALE_TYPE.FIXED_EXTENT) {
1562
                    getMapContext().getViewPort().setEnvelope(getFixedExtent());
1563
                } else {
1564
                    Envelope newEnvelope;
1565
                    if (getScaleType() == SCALE_TYPE.FIXED_SCALE && getFixedScale() != null) {
1566
                        getMapContext().setScaleView(Math.round(getFixedScale()));
1567
                        newEnvelope = getMapContext().getViewPort().getAdjustedEnvelope();
1568
                    } else {
1569
                        newEnvelope = e.getNewExtent();
1570
                    }
1571
                    if (getView() != null) {
1572
                        if (getExtentSynced()) {
1573
                            getView().getMapContext().getViewPort().setEnvelope(newEnvelope);
1574
                        }
1575
                    }
1576
                }
1577
                updateScaleCtrl();
1578
                invalidateLayout();
1579
            } finally {
1580
                endUpdate();
1581
            }
1582
        }
1583

    
1584
        @Override
1585
        public void backColorChanged(ColorEvent e) {
1586
            if (!isUpdating()) {
1587
                if (getView() != null) {
1588
                    try {
1589
                        beginUpdate();
1590
                        if (getLayerSynced()) {
1591
                            getView().getMapContext().getViewPort().setBackColor(e.getNewColor());
1592
                        }
1593
                        invalidateLayout();
1594
                    } finally {
1595
                        endUpdate();
1596
                    }
1597
                }
1598
            }
1599
        }
1600

    
1601
        @Override
1602
        public void projectionChanged(ProjectionEvent e) {
1603
            if (!isUpdating() && getExtentSynced()) {
1604
                if (getView() != null) {
1605
                    try {
1606
                        beginUpdate();
1607
                        if (getLayerSynced()) {
1608
                            getView().getMapContext().getViewPort().setProjection(e.getNewProjection());
1609
                        }
1610
                        invalidateLayout();
1611
                        // FIXME: force also a view redraw someway??
1612
                    } finally {
1613
                        endUpdate();
1614
                    }
1615
                }
1616
            }
1617
        }
1618

    
1619
        public void conditionalRedraw() {
1620
            if (!isUpdating()) {
1621
                try {
1622
                    beginUpdate();
1623
                    invalidateLayout();
1624
                    // the view should also receive the event and update automatically
1625
                } finally {
1626
                    endUpdate();
1627
                }
1628
            }
1629
        }
1630

    
1631
        @Override
1632
        public void legendChanged(final LegendChangedEvent e) {
1633
            conditionalRedraw();
1634
            refreshToc();
1635
        }
1636

    
1637
        @Override
1638
        public void layerAdded(final LayerCollectionEvent e) {
1639
            // necessary to set an envelope when the first layer is added
1640
            if (!isUpdating() && getMapContext().getViewPort().getEnvelope() == null) {
1641
                try {
1642
                    beginUpdate();
1643
                    fullExtent();
1644

    
1645
                } catch (ReadException e1) {
1646
                } finally {
1647
                    endUpdate();
1648
                }
1649
            }
1650
            conditionalRedraw();
1651
            refreshToc();
1652
        }
1653

    
1654
        @Override
1655
        public void layerMoved(final LayerPositionEvent e) {
1656
            conditionalRedraw();
1657
            refreshToc();
1658
        }
1659

    
1660
        @Override
1661
        public void layerRemoved(final LayerCollectionEvent e) {
1662
            conditionalRedraw();
1663
            refreshToc();
1664
        }
1665

    
1666
        @Override
1667
        public void layerAdding(LayerCollectionEvent e)
1668
                throws CancelationException {
1669
            // nothing neededO
1670

    
1671
        }
1672

    
1673
        @Override
1674
        public void layerMoving(LayerPositionEvent e)
1675
                throws CancelationException {
1676
            // nothing needed
1677

    
1678
        }
1679

    
1680
        @Override
1681
        public void layerRemoving(LayerCollectionEvent e)
1682
                throws CancelationException {
1683
            // nothing needed
1684

    
1685
        }
1686

    
1687
        @Override
1688
        public void visibilityChanged(LayerCollectionEvent e)
1689
                throws CancelationException {
1690
            conditionalRedraw();
1691
        }
1692
    }
1693

    
1694
}