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

History | View | Annotate | Download (58.8 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
            gimg.setBackground(viewPort.getBackColor());
524
            gimg.fillRect(0, 0, width, height);
525
            getMapContext().draw(m_image, gimg, getScale());
526
            gimg.dispose();
527
            b_validCache = true;
528
        }
529

    
530
    }
531

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

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

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

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

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

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

    
587
    }
588

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

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

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

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

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

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

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

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

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

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

    
665
    }
666

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

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

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

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

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

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

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

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

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

    
753
    @Override
754
    public String getName() {
755
        return PERSISTENCE_DEFINITION_NAME;
756
    }
757

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

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

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

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

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

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

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

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

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

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

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

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

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

    
862
    public IFFrame getGrid() {
863
        return this.grid;
864
    }
865

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

    
874
    public void showGrid(boolean b) {
875
        showGrid = b;
876
    }
877

    
878
    public boolean isShowGrid() {
879
        return showGrid;
880
    }
881

    
882
    @Override
883
    public void refreshOriginalExtent() {
884
    }
885

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1114
    }
1115

    
1116
    protected void resetAllListeners() {
1117
        this.removeAllListeners();
1118
        this.addAllListeners();
1119
    }
1120

    
1121
    protected void removeAllListeners() {
1122
        removeOwnListeners();
1123
        removeViewListeners();
1124
    }
1125

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

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

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

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

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

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

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

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

    
1207
    @Override
1208
    public boolean getLayerSynced() {
1209
        return syncLayers;
1210
    }
1211

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

    
1218
    @Override
1219
    public boolean getExtentSynced() {
1220
        return syncExtents;
1221
    }
1222

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

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

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

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

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

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

    
1292
    protected boolean isDrawing() {
1293
        return this.b_drawing;
1294
    }
1295

    
1296
    protected boolean isUpdating() {
1297
        return this.b_updating;
1298
    }
1299

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

    
1308
    protected boolean isSyncLayers() {
1309
        return this.syncLayers;
1310
    }
1311

    
1312
    protected void beginUpdate() {
1313
        this.b_updating = true;
1314
    }
1315

    
1316
    protected void endUpdate() {
1317
        this.b_updating = false;
1318
    }
1319

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

    
1325
    public Double getFixedScale() {
1326
        return this.fixedScale;
1327
    }
1328

    
1329
    public Envelope getFixedExtent() {
1330
        return this.fixedExtent;
1331
    }
1332

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

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

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

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

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

    
1366
    @Deprecated
1367
    @Override
1368
    public void setLinked(boolean b) {
1369
    }
1370

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

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

    
1386
    }
1387

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

    
1396
        public ViewDocListener(FFrameView fview) {
1397

    
1398
        }
1399

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1545
    private class OwnMapContextListener
1546
            implements ViewPortListener, LegendListener, LayerCollectionListener {
1547

    
1548
        public OwnMapContextListener(FFrameView fview) {
1549

    
1550
        }
1551

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

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

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

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

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

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

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

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

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

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

    
1673
        }
1674

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

    
1680
        }
1681

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

    
1687
        }
1688

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

    
1696
}