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

History | View | Annotate | Download (60.4 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
import org.cresques.cts.IProjection;
36
import org.gvsig.andami.PluginServices;
37
import org.gvsig.andami.ui.mdiFrame.NewStatusBar;
38
import org.gvsig.app.project.Project;
39
import org.gvsig.app.project.ProjectManager;
40
import org.gvsig.app.project.documents.layout.DefaultLayoutNotification;
41
import org.gvsig.app.project.documents.layout.FLayoutFunctions;
42
import org.gvsig.app.project.documents.layout.FLayoutUtilities;
43
import org.gvsig.app.project.documents.layout.LayoutNotification;
44
import org.gvsig.app.project.documents.layout.TocModelChangedNotification;
45
import org.gvsig.app.project.documents.view.ViewDocument;
46
import org.gvsig.compat.print.PrintAttributes;
47
import org.gvsig.fmap.dal.exception.ReadException;
48
import org.gvsig.fmap.geom.Geometry;
49
import org.gvsig.fmap.geom.Geometry.SUBTYPES;
50
import org.gvsig.fmap.geom.GeometryLocator;
51
import org.gvsig.fmap.geom.GeometryManager;
52
import org.gvsig.fmap.geom.exception.CreateEnvelopeException;
53
import org.gvsig.fmap.geom.primitive.Envelope;
54
import org.gvsig.fmap.mapcontext.MapContext;
55
import org.gvsig.fmap.mapcontext.MapContextException;
56
import org.gvsig.fmap.mapcontext.ViewPort;
57
import org.gvsig.fmap.mapcontext.events.AtomicEvent;
58
import org.gvsig.fmap.mapcontext.events.ColorEvent;
59
import org.gvsig.fmap.mapcontext.events.ExtentEvent;
60
import org.gvsig.fmap.mapcontext.events.FMapEvent;
61
import org.gvsig.fmap.mapcontext.events.ProjectionEvent;
62
import org.gvsig.fmap.mapcontext.events.listeners.AtomicEventListener;
63
import org.gvsig.fmap.mapcontext.events.listeners.ViewPortListener;
64
import org.gvsig.fmap.mapcontext.layers.CancelationException;
65
import org.gvsig.fmap.mapcontext.layers.FLayer;
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.layers.vectorial.FLyrVect;
71
import org.gvsig.fmap.mapcontext.rendering.legend.events.LegendChangedEvent;
72
import org.gvsig.fmap.mapcontext.rendering.legend.events.listeners.LegendListener;
73
import org.gvsig.gui.beans.Messages;
74
import org.gvsig.tools.ToolsLocator;
75
import org.gvsig.tools.dispose.DisposeUtils;
76
import org.gvsig.tools.dynobject.DynStruct;
77
import org.gvsig.tools.persistence.PersistenceManager;
78
import org.gvsig.tools.persistence.PersistentState;
79
import org.gvsig.tools.persistence.exception.PersistenceException;
80
import org.slf4j.Logger;
81
import org.slf4j.LoggerFactory;
82

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

    
106
    private static final Logger logger = LoggerFactory.getLogger(FFrameView.class);
107

    
108
    public static final String PERSISTENCE_DEFINITION_NAME = "FFrameView";
109

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

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

    
131
    public static final int PRESENTATION = 0;
132
    public static final int DRAFT = 1;
133

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

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

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

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

    
166
    protected AffineTransform originalGraphicsAT = null;
167
    protected Rectangle originalClip = null;
168

    
169
    private SCALE_TYPE scaleType = SCALE_TYPE.NORMAL;
170
    private Double fixedScale = null;
171
    private Envelope fixedExtent = null;
172
    private boolean persistIndependent;
173

    
174
    /**
175
     * Creates a new FFrameView object.
176
     */
177
    public FFrameView() {
178
        num++;
179
//        logger.info("Create FFrameView "+ Integer.toHexString(this.hashCode()));
180
        createListeners();
181
    }
182

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

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

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

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

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

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

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

    
272
    /**
273
     * Sets the MapContext associated with this FFrameView, which will be used
274
     * to clone the layers and synchronize the FFrameView with the associated
275
     * View
276
     *
277
     * @param viewMapContext
278
     */
279
    public void setViewMapContext(MapContext viewMapContext) {
280
        Envelope oldEnvelope = null;
281
        if (this.mapContext != null) {
282
            removeOwnListeners();
283
            if (this.mapContext.getViewPort() != null) {
284
                oldEnvelope = this.mapContext.getViewPort().getEnvelope();
285
            }
286
            DisposeUtils.disposeQuietly(this.mapContext);
287
            this.mapContext = null;
288
        }
289
        if (viewMapContext == null) { // disconnect the view from the map
290
            this.mapContext = null;
291
            return;
292
        }
293

    
294
        try {
295
            if (syncLayers) {
296
                mapContext
297
                        = viewMapContext.createNewFMap(
298
                                (ViewPort) viewMapContext.getViewPort().clone());
299
                for (FLayer fLayer : viewMapContext.getGraphicsLayers()) {
300
                    if (fLayer instanceof FLyrVect) {
301
                        mapContext.setGraphicsLayer(fLayer.getName(), (FLyrVect) fLayer);
302
                    }
303
                }
304

    
305
            } else {
306
                mapContext = viewMapContext.cloneFMap();
307
                mapContext.setViewPort((ViewPort) viewMapContext
308
                        .getViewPort().clone());
309
            }
310
            ViewPort newViewPort = getMapContext().getViewPort();
311
            if (!syncExtents && oldEnvelope != null) {
312
                // if extent is not synced with the view, restore the previous
313
                // envelope if existing
314
                newViewPort.setEnvelope(oldEnvelope);
315
            }
316
            AffineTransform at;
317
            if (getLayoutContext() != null) {
318
                at = getLayoutContext().getAT();
319
            } else {
320
                at = null;
321
            }
322
            newViewPort.setImageSize(new Dimension((int) getBoundingBox(at).width,
323
                    (int) getBoundingBox(at).height));
324
            newViewPort.setDPI(getDrawPaperDPI());
325
            addAllListeners();
326
            updateScaleCtrl();
327
            setTocModel();
328
        } catch (CloneNotSupportedException e1) {
329
            logger.warn("Can't set mapcontext of the fframeview", e1);
330
        }
331

    
332
    }
333

    
334
    /**
335
     * Sets the View associated with this FFrameView, which will be used to
336
     * clone the MapContext and the layers. It will also used to synchronize the
337
     * FFrameView with the associated View, depending on the selected scale type
338
     *
339
     * @param viewDocument
340
     */
341
    public void setView(ViewDocument viewDocument) {
342
        removeViewListeners();
343
        this.viewDocument = viewDocument;
344
        if (this.viewDocument != null) {
345
            setViewMapContext(this.viewDocument.getMapContext());
346
        } else {
347
            setViewMapContext(null);
348
        }
349
    }
350

    
351
    /**
352
     * Gets the associated View
353
     *
354
     * @return The associated view
355
     * @see {@link #setView(ViewDocument)}
356
     */
357
    public ViewDocument getView() {
358
        return viewDocument;
359
    }
360

    
361
    /**
362
     * Draws the FFrameView on the provided Graphics, according to the provided
363
     * affine transform and the visible rectangle.
364
     *
365
     * @param g Graphics2D
366
     * @param at Affine transform to translate sheet coordinates (in cm) to
367
     * screen coordinates (in pixels)
368
     * @param visibleLayoutDocRect visible rectangle
369
     * @param imgBase Image used to speed up the drawing process
370
     */
371
    @Override
372
    public void draw(Graphics2D g, AffineTransform at, Rectangle2D visibleLayoutDocRect, BufferedImage imgBase) {
373
        Rectangle2D.Double fframeViewRect = getBoundingBox(at);
374
        Rectangle2D.Double visibleArea = (Rectangle2D.Double) getVisibleRect(visibleLayoutDocRect, fframeViewRect);
375
        if (visibleArea == null) {
376
            return;
377
        }
378
        preDraw(g, fframeViewRect, visibleArea);
379
        if (getMapContext() == null) {
380
            drawEmpty(g);
381
        } else {
382
            if (FLayoutUtilities.hasEditingLayers(getView())) {
383
                // We are not drawing if any layer is in editing mode
384
                drawMessage(g, Messages.getText(
385
                        "_Cannot_draw_view_if_layers_in_editing_mode"));
386

    
387
            } else {
388
                if (getQuality() == PRESENTATION) {
389
                    try {
390
                        drawPresentation(g, at, fframeViewRect, visibleArea, imgBase);
391
                    } catch (Exception exc) {
392
                        drawMessage(g, FLayoutFunctions.getLastMessage(exc));
393
                    }
394

    
395
                } else {
396
                    drawDraft(g);
397
                }
398
            }
399
        }
400
        postDraw(g, fframeViewRect, at);
401
        if (showGrid && grid != null) {
402
            grid.draw(g, at, visibleLayoutDocRect, imgBase);
403
        }
404
    }
405

    
406
    private void drawMessage(Graphics2D g, String msg) {
407

    
408
        Rectangle2D r = getBoundingBox(null);
409
        g.setColor(Color.lightGray);
410
        g.fillRect((int) r.getX(), (int) r.getY(), (int) r.getWidth(), (int) r.getHeight());
411
        g.setColor(Color.darkGray);
412
        g.setStroke(new BasicStroke(2));
413
        g.drawRect((int) r.getX(), (int) r.getY(), (int) r.getWidth(), (int) r.getHeight());
414
        g.setColor(Color.black);
415

    
416
        int scale = (int) (r.getWidth() / 24);
417
        Font f = new Font("SansSerif", Font.PLAIN, scale);
418
        g.setFont(f);
419
        if (msg == null) {
420
            msg = Messages.getText("error");
421
        }
422
        g.drawString(msg, (int) (r.getCenterX() - ((msg.length() * scale) / 4)),
423
                (int) (r.getCenterY())
424
        );
425
    }
426

    
427
    /**
428
     * Gets the visible envelope, in map coordinates
429
     *
430
     * @param fframeViewRect Rectangle defining the bounding box of the
431
     * FFrameView, in screen coordinates
432
     * @param visiblefframeViewRect Rectangle defining the bounding box of the
433
     * visible area of the fframeView, in screen coordinates
434
     * @return
435
     */
436
    protected Envelope getVisibleEnvelope(Rectangle2D.Double fframeViewRect,
437
            Rectangle2D.Double visiblefframeViewRect) {
438
        Envelope oldEnv = getMapContext().getViewPort().getAdjustedEnvelope();
439
        double widthFactor = ((int) visiblefframeViewRect.width) / fframeViewRect.width;
440
        double heightFactor = ((int) visiblefframeViewRect.height) / fframeViewRect.height;
441

    
442
        double newWidth = oldEnv.getLength(0) * widthFactor;
443
        double newHeight = oldEnv.getLength(1) * heightFactor;
444

    
445
        double translateX = visiblefframeViewRect.x - fframeViewRect.x;
446
        double translateY = visiblefframeViewRect.y - fframeViewRect.y;
447
        double translateFactorX = translateX / fframeViewRect.width;
448
        double translateFactorY = translateY / fframeViewRect.height;
449

    
450
        double newX = oldEnv.getMinimum(0) + translateFactorX * oldEnv.getLength(0);
451
        double newMaxY = oldEnv.getMaximum(1) - translateFactorY * oldEnv.getLength(1);
452
        double newMaxX = newX + newWidth;
453
        double newY = newMaxY - newHeight;
454

    
455
        Envelope newEnv = null;
456
        try {
457
            newEnv = geomManager.createEnvelope(newX, newY, newMaxX, newMaxY, SUBTYPES.GEOM2D);
458
        } catch (CreateEnvelopeException ex) {
459
            logger.warn("Can't calculate the envelope of the visible area.", ex);
460
        }
461
        return newEnv;
462

    
463
    }
464

    
465
    protected void drawPresentation(
466
            Graphics2D g,
467
            AffineTransform affineTransform,
468
            Rectangle2D.Double fframeViewRect,
469
            Rectangle2D.Double visibleRect,
470
            BufferedImage imgBase) throws Exception {
471

    
472
        b_drawing = true;
473
        int drawWidth = (int) visibleRect.width;
474
        int drawHeight = (int) visibleRect.height;
475

    
476
        Envelope oldEnvelope = null;
477
        if (!visibleRect.equals(fframeViewRect)) {
478
            // if visible area is smaller than the fframe, we will only draw this area,
479
            // so we need to tell the ViewPort the image size and extent for drawing,
480
            // and restore the real extent after drawing
481
            oldEnvelope = getMapContext().getViewPort().getEnvelope();
482
            if (oldEnvelope == null) {
483
                return;
484
            }
485

    
486
            Envelope newEnvelope = getVisibleEnvelope(fframeViewRect, visibleRect);
487
            // image size must be set before the envelope, as it has influence on the adjustedExtent
488
            getMapContext().getViewPort().setImageSize(new Dimension(drawWidth, drawHeight));
489
            getMapContext().getViewPort().setEnvelope(newEnvelope);
490
        } else {
491
            getMapContext().getViewPort().setImageSize(new Dimension(drawWidth, drawHeight));
492
            getMapContext().getViewPort().refreshExtent();
493
        }
494

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

    
498
        // paint the MapContext on m_image, if not already cached
499
        createImage(affineTransform, drawWidth, drawHeight, mapOrigin);
500

    
501
        //Draw the created image
502
        drawImage(g, m_image, visibleRect);
503

    
504
        if (oldEnvelope != null) {
505
            // restore real envelope and image size
506
            getMapContext().getViewPort().setImageSize(new Dimension((int) fframeViewRect.width, (int) fframeViewRect.height));
507
            getMapContext().getViewPort().setEnvelope(oldEnvelope);
508
        }
509

    
510
        scaleAnt = affineTransform.getScaleX();
511
        origin = mapOrigin;
512
        b_drawing = false;
513
    }
514

    
515
    protected void createImage(AffineTransform affineTransform,
516
            int width, int height, Point mapOrigin) throws ReadException, MapContextException {
517
        ViewPort viewPort = this.getMapContext().getViewPort();
518

    
519
        //If the image has to be created...
520
        if (origin == null
521
                || !origin.equals(mapOrigin)
522
                || affineTransform.getScaleX() != scaleAnt
523
                || m_image == null
524
                || !b_validCache) {
525

    
526
            viewPort.setDPI(getDrawPaperDPI());
527
            viewPort.setImageSize(new Dimension(width, height));
528

    
529
            m_image = new BufferedImage(
530
                    width,
531
                    height,
532
                    BufferedImage.TYPE_INT_ARGB
533
            );
534

    
535
            Graphics2D gimg = (Graphics2D) m_image.createGraphics();
536
            Color saved_color = gimg.getColor();
537
            gimg.setBackground(viewPort.getBackColor());
538
            gimg.setColor(viewPort.getBackColor());
539
            gimg.fillRect(0, 0, width, height);
540
            gimg.setColor(saved_color);
541
            getMapContext().draw(m_image, gimg, getScale());
542
            gimg.dispose();
543
            b_validCache = true;
544
        }
545

    
546
    }
547

    
548
    protected void drawImage(Graphics2D g, BufferedImage image,
549
            Rectangle2D.Double visibleRectangle) {
550

    
551
        Color theBackColor = getMapContext().getViewPort().getBackColor();
552
        if (theBackColor != null) {
553
            g.setColor(theBackColor);
554
            g.fillRect((int) visibleRectangle.x, (int) visibleRectangle.y,
555
                    (int) visibleRectangle.width,
556
                    (int) visibleRectangle.height);
557
        }
558
        g.drawImage(m_image,
559
                (int) visibleRectangle.x,
560
                (int) visibleRectangle.y,
561
                null);
562
    }
563

    
564
    protected void preDraw(Graphics2D g, Rectangle2D.Double fframeViewRect, Rectangle2D.Double visibleRect) {
565
        originalGraphicsAT = (AffineTransform) g.getTransform().clone();
566

    
567
        if (g.getClipBounds() != null) {
568
            originalClip = (Rectangle) g.getClipBounds().clone();
569
        }
570
        AffineTransform rotationAT = getRotationAT();
571
        if (rotationAT != null) {
572
            g.transform(rotationAT);
573
        }
574
        g.setClip((int) visibleRect.getMinX(), (int) visibleRect.getMinY(),
575
                (int) visibleRect.getWidth(), (int) visibleRect.getHeight());
576
    }
577

    
578
    protected void postDraw(Graphics2D g, Rectangle2D.Double fframeViewRect, AffineTransform at) {
579
        g.setTransform(originalGraphicsAT);
580
        if (getMapContext() != null) {
581
            setATMap(getMapContext().getViewPort().getAffineTransform());
582
        }
583
        if (originalClip != null) {
584
            g.setClip(originalClip.x, originalClip.y, originalClip.width, originalClip.height);
585
        }
586
    }
587

    
588
    /**
589
     * @param g
590
     * @param rectangleLayout
591
     * @param rectangleView
592
     * @param originalClip
593
     * @param imgBase
594
     * @param at
595
     * @deprecated Use
596
     * {@link #postDraw(Graphics2D, java.awt.geom.Rectangle2D.Double, AffineTransform)}
597
     * instead.
598
     */
599
    @Deprecated
600
    protected void postDraw(Graphics2D g, Rectangle2D.Double rectangleLayout, Rectangle2D rectangleView, BufferedImage imgBase, Rectangle originalClip, AffineTransform at) {
601
        postDraw(g, rectangleLayout, at);
602

    
603
    }
604

    
605
    /**
606
     * @param g
607
     * @param rectangleLayout
608
     * @return
609
     * @deprecated Use {@link #preDraw(Graphics2D, java.awt.geom.Rectangle2D.Double, java.awt.geom.Rectangle2D.Double) instead
610
     */
611
    @Deprecated
612
    protected Rectangle preDraw(Graphics2D g, Rectangle2D.Double rectangleLayout) {
613
        Rectangle originalClip = null;
614
        if (g.getClipBounds() != null) {
615
            originalClip = (Rectangle) g.getClipBounds().clone();
616
        }
617
        preDraw(g, rectangleLayout, rectangleLayout);
618
        return originalClip;
619
    }
620

    
621
    @Override
622
    public void print(Graphics2D g, AffineTransform at, Geometry geom,
623
            PrintAttributes printAttributes) {
624
        Rectangle2D.Double rectangleLayout = getBoundingBox(at);
625

    
626
        preDraw(g, rectangleLayout, rectangleLayout);
627
        print(g, at, printAttributes);
628
        postDraw(g, rectangleLayout, at);
629
        if (showGrid && grid != null) {
630
            grid.print(g, at, geom, printAttributes);
631
        }
632
    }
633

    
634
    protected void print(Graphics2D g, AffineTransform at, PrintAttributes printAttributes) {
635
        Rectangle2D.Double layoutRectangle = getBoundingBox(at);
636

    
637
        // FIXME: should we clone the mapcontext and viewport before printing ??
638
        // otherwise we will probably have unexpected results if the user modifies
639
        // the layout while printing (answer: not an issue at the moment as printing is
640
        // a blocking operation)
641
        ViewPort viewPort = this.getMapContext().getViewPort();
642

    
643
        Point2D old_offset = viewPort.getOffset();
644
        Dimension old_imgsize = viewPort.getImageSize();
645
        double oldDpi = viewPort.getDPI();
646

    
647
        viewPort.setOffset(new Point2D.Double(layoutRectangle.x, layoutRectangle.y));
648
        viewPort.setImageSize(
649
                new Dimension(
650
                        (int) layoutRectangle.width,
651
                        (int) layoutRectangle.height
652
                )
653
        );
654
        double dpi = PrintAttributes.PRINT_QUALITY_DPI[printAttributes.getPrintQuality()];
655
        viewPort.setDPI(dpi);
656

    
657
        //Draw the backgroung color of the map
658
        Color theBackColor = viewPort.getBackColor();
659
        if (theBackColor != null) {
660
            g.setColor(theBackColor);
661
            g.fillRect(
662
                    (int) layoutRectangle.x,
663
                    (int) layoutRectangle.y,
664
                    viewPort.getImageWidth(),
665
                    viewPort.getImageHeight()
666
            );
667
        }
668

    
669
        //Print the map
670
        try {
671
            this.getMapContext().print(g, getScale(), printAttributes);
672
        } catch (Exception ex) {
673
            logger.warn("Can't print frame view.", ex);
674
        }
675

    
676
        // Restore offset, imgsize
677
        viewPort.setOffset(old_offset);
678
        viewPort.setImageSize(old_imgsize);
679
        viewPort.setDPI(oldDpi);
680

    
681
    }
682

    
683
    /**
684
     * Rellena la unidad de medida en la que est? la vista.
685
     *
686
     * @param i entero que representa la unidad de medida de la vista.
687
     */
688
    public void setMapUnits(int i) {
689
        mapUnits = i;
690
    }
691

    
692
    /**
693
     * Obtiene la unidad de medida en la que est? la vista.
694
     *
695
     * @return Unidad de medida.
696
     */
697
    public int getMapUnits() {
698
        return mapUnits;
699
    }
700

    
701
    /**
702
     * Devuelve la escala seg?n el tipo de escala que se haya seleccionado al
703
     * a?adida la vista.
704
     *
705
     * @return escala.
706
     */
707
    public long getScale() {
708
        return (long) getMapContext().getScaleView();
709
    }
710

    
711
    /**
712
     * Inserta la imagen para repintar el FFrameView.
713
     *
714
     * @param bi Imagen para repintar.
715
     */
716
    public void setBufferedImage(BufferedImage bi) {
717
        m_image = bi;
718
    }
719

    
720
    /**
721
     * Devuelve la imagen para repintar.
722
     *
723
     * @return Imagen para repintar.
724
     */
725
    @Override
726
    public BufferedImage getBufferedImage() {
727
        return m_image;
728
    }
729

    
730
    /**
731
     * Devuelve la MAtriz de transformaci?n utilizada por la FFrameView.
732
     *
733
     * @return MAtriz de transformaci?n.
734
     */
735
    @Override
736
    public AffineTransform getATMap() {
737
        return mapAT;
738
    }
739

    
740
    /**
741
     * Inserta la matriz de transformaci?n.
742
     *
743
     * @param transform Matriz de transformaci?n.
744
     */
745
    @Override
746
    public void setATMap(AffineTransform transform) {
747
        mapAT = transform;
748
    }
749

    
750
    /**
751
     * Inserta el proyecto.
752
     *
753
     * @param p Proyecto.
754
     */
755
    @Override
756
    public void setProject(Project p) {
757
        project = p;
758
    }
759

    
760
    /**
761
     * @see
762
     * org.gvsig.app.project.documents.layout.fframes.IFFrame#getNameFFrame()
763
     */
764
    @Override
765
    public String getNameFFrame() {
766
        return PluginServices.getText(this, "Vista") + num;
767
    }
768

    
769
    @Override
770
    public String getName() {
771
        return PERSISTENCE_DEFINITION_NAME;
772
    }
773

    
774
    public boolean compare(Object arg0) {
775
        if (!(arg0 instanceof FFrameView)) {
776
            return false;
777
        }
778

    
779
        if (!this.getName().equals(((FFrameView) arg0).getName())) {
780
            return false;
781
        }
782

    
783
        if (Math.abs(this.getBoundBox().getWidth()
784
                - (((FFrameView) arg0).getBoundBox().getWidth())) > 0.05) {
785
            return false;
786
        }
787
        if (Math.abs(this.getBoundBox().getHeight()
788
                - (((FFrameView) arg0).getBoundBox().getHeight())) > 0.05) {
789
            return false;
790
        }
791

    
792
        if (!this.toString().equals(((FFrameView) arg0).toString())) {
793
            return false;
794
        }
795

    
796
        if (this.getMapContext() != null
797
                && !this.getMapContext()
798
                        .equals(((FFrameView) arg0).getMapContext())) {
799
            return false;
800
        }
801

    
802
        if (this.getRotation() != ((FFrameView) arg0).getRotation()) {
803
            return false;
804
        }
805
        return true;
806
    }
807

    
808
    public void updateScaleCtrl() {
809
        NewStatusBar statusbar = PluginServices.getMainFrame().getStatusBar();
810
        MapContext mapContext = this.getMapContext();
811
        if (mapContext == null) {
812
            return;
813
        }
814
        statusbar.setMessage("units",
815
                PluginServices.getText(this, mapContext.getDistanceName())
816
        );
817
        String scale;
818
        if (fixedScale != null && getScaleType() == SCALE_TYPE.FIXED_SCALE) {
819
            // prefer fixedScale as getScaleView() may offer slight differences
820
            // because the influence of the adjusted envelope
821
            scale = String.valueOf(fixedScale.longValue());
822
        } else {
823
            scale = String.valueOf(getMapContext().getScaleView());
824
        }
825
        statusbar.setControlValue("layout-view-change-scale",
826
                scale);
827
        IProjection proj = mapContext.getViewPort().getProjection();
828
        if (proj != null) {
829
            statusbar.setMessage("projection", proj.getAbrev());
830
        } else {
831
            statusbar.setMessage("projection", "");
832
        }
833
    }
834

    
835
    @Override
836
    public void fullExtent() throws ReadException {
837
        setNewEnvelope(getMapContext().getFullEnvelope());
838
    }
839

    
840
    @Override
841
    public void setPointsToZoom(Point2D px1, Point2D px2) {
842
        p1 = px1;
843
        p2 = px2;
844
    }
845

    
846
    @Override
847
    public void movePoints(Point2D px1, Point2D px2) {
848
        double difX = -px2.getX() + px1.getX();
849
        double difY = -px2.getY() + px1.getY();
850
        if (p1 != null) {
851
            p1.setLocation(p1.getX() + difX, p1.getY() + difY);
852
            p2.setLocation(p2.getX() + difX, p2.getY() + difY);
853
        }
854
    }
855

    
856
    @Override
857
    public FFrameView clone() throws CloneNotSupportedException {
858
//        logger.info("clone FFrameView "+ Integer.toHexString(this.hashCode()));
859
        FFrameView frame = (FFrameView) super.clone();
860
        frame.createListeners(); // necessary to create the listeners within the right scope
861
        frame.setView(this.getView());
862
        frame.removeAllListeners(); // remove listeners add by setView
863

    
864
        if (grid != null) {
865
            FFrameGrid newGrid = (FFrameGrid) this.grid.clone();
866
            newGrid.setFFrameDependence(frame);
867
            frame.setGrid(newGrid);
868
        }
869
//        logger.info("cloned FFrameView "+ Integer.toHexString(frame.hashCode()));
870
        return frame;
871
    }
872

    
873
    public void setGrid(IFFrame grid) {
874
        this.grid = grid;
875
        this.grid.setRotation(this.getRotation());
876
    }
877

    
878
    public IFFrame getGrid() {
879
        return this.grid;
880
    }
881

    
882
    @Override
883
    public void setRotation(double rotation) {
884
        super.setRotation(rotation);
885
        if (grid != null) {
886
            grid.setRotation(rotation);
887
        }
888
    }
889

    
890
    public void showGrid(boolean b) {
891
        showGrid = b;
892
    }
893

    
894
    public boolean isShowGrid() {
895
        return showGrid;
896
    }
897

    
898
    @Override
899
    public void refreshOriginalExtent() {
900
    }
901

    
902
    public static void registerPersistent() {
903
        PersistenceManager manager = ToolsLocator.getPersistenceManager();
904
        if (manager.getDefinition(PERSISTENCE_DEFINITION_NAME) == null) {
905
            DynStruct definition = manager.addDefinition(
906
                    FFrameView.class,
907
                    PERSISTENCE_DEFINITION_NAME,
908
                    "FFrameView persistence definition", null, null
909
            );
910
            definition.extend(
911
                    manager.getDefinition(FFrame.PERSISTENCE_DEFINITION_NAME)
912
            );
913
            definition.addDynFieldInt(QUALITY_FIELD).setMandatory(true);
914
            definition.addDynFieldInt(MAPUNITS_FIELD).setMandatory(true);
915
            definition.addDynFieldDouble(SCALE_FIELD).setMandatory(false);
916
            definition.addDynFieldObject(VIEW_FIELD)
917
                    .setClassOfValue(ViewDocument.class).setMandatory(false);
918
            definition.addDynFieldObject(ENVELOPE_FIELD)
919
                    .setClassOfValue(Envelope.class).setMandatory(false);
920
            definition.addDynFieldBoolean(SHOWGRID_FIELD).setMandatory(true);
921
            definition.addDynFieldObject(GRID_FIELD)
922
                    .setClassOfValue(IFFrame.class).setMandatory(false);
923
            definition.addDynFieldBoolean(HAS_TOC_FIELD).setMandatory(false);
924
            definition.addDynFieldBoolean(EXTENT_SYNC_FIELD).setMandatory(false);
925
            definition.addDynFieldBoolean(LAYER_SYNC_FIELD).setMandatory(false);
926
            definition.addDynFieldInt(SCALE_TYPE_FIELD).setMandatory(false);
927
            // unused fields, kept for backward compatibility
928
            definition.addDynFieldInt(MODE_FIELD).setMandatory(false);
929
            definition.addDynFieldInt(TYPESCALE_FIELD).setMandatory(false);
930
            definition.addDynFieldBoolean(BLINKED_FIELD).setMandatory(false);
931
            definition.addDynFieldObject(MAPCONTEXT_FIELD)
932
                    .setClassOfValue(MapContext.class).setMandatory(false);
933
            definition.addDynFieldInt(VIEWING_FIELD).setMandatory(false);
934
            definition.addDynFieldInt(EXTENSION_FIELD).setMandatory(false);
935
            definition.addDynFieldBoolean(PERSIST_INDEPENDENT_FIELD).setMandatory(false);
936
        }
937
    }
938

    
939
    @Override
940
    public void loadFromState(PersistentState state)
941
            throws PersistenceException {
942
        super.loadFromState(state);
943
        this.persistIndependent = state.getBoolean(PERSIST_INDEPENDENT_FIELD, false);
944
        b_frameInitialized = false;
945
        if (state.hasValue(EXTENT_SYNC_FIELD)) {
946
            syncExtents = state.getBoolean(EXTENT_SYNC_FIELD);
947
        } else {
948
            syncExtents = true;
949
        }
950
        if (state.hasValue(LAYER_SYNC_FIELD)) {
951
            syncLayers = state.getBoolean(LAYER_SYNC_FIELD);
952
        } else {
953
            syncLayers = true;
954
        }
955
        Double layoutScale = null;
956
        if (state.hasValue(SCALE_FIELD)) {
957
            layoutScale = state.getDouble(SCALE_FIELD);
958
        }
959
        Envelope envelope = (Envelope) state.get(ENVELOPE_FIELD);
960

    
961
        if (state.hasValue(SCALE_TYPE_FIELD)) {
962
            int value = state.getInt(SCALE_TYPE_FIELD);
963
            if (value == SCALE_TYPE.FIXED_EXTENT.ordinal()) {
964
                scaleType = SCALE_TYPE.FIXED_EXTENT;
965
                fixedExtent = envelope;
966
            } else if (value == SCALE_TYPE.FIXED_SCALE.ordinal()) {
967
                scaleType = SCALE_TYPE.FIXED_SCALE;
968
                fixedScale = layoutScale;
969
            }
970
            // else use the default value
971
        }
972
        quality = state.getInt(QUALITY_FIELD);
973
        mapUnits = state.getInt(MAPUNITS_FIELD);
974
        if (state.hasValue(HAS_TOC_FIELD)) {
975
            this.b_hasToc = state.getBoolean(HAS_TOC_FIELD);
976
        }
977

    
978
        // When copying and pasting layout documents (in project manager), the view
979
        // is first persisted (copy action) and then re-created from persistence (paste action).
980
        // This is a problem, as the re-created view is not the same view as the original one
981
        // and thus layout stops being synchronized with the right view.
982
        // Therefore, we try to get first the "live" view if available, and get the persisted
983
        // one otherwise, which will be used when opening projects.
984
        ViewDocument persistenceView = (ViewDocument) state.get(VIEW_FIELD);
985
        if(this.isPersistIndependent()){
986
            this.syncExtents = false;
987
            this.syncLayers = false;
988
            this.setView(persistenceView);
989
        } else if (persistenceView != null) {
990
            ViewDocument view = (ViewDocument) ProjectManager.getInstance().getCurrentProject().getDocument(persistenceView.getName(), persistenceView.getTypeName());
991
            if (view == null) {
992
                view = persistenceView;
993
            } else {
994
                DisposeUtils.disposeQuietly(persistenceView.getMapOverViewContext());
995
                DisposeUtils.disposeQuietly(persistenceView.getMapContext());
996
                DisposeUtils.disposeQuietly(persistenceView);
997
            }
998
            this.setView(view);
999
            // it is crucial to don't persist the MapContext and get a cloned one from the View instead,
1000
            // as the cloned instance is different from the one created using persistence. In particular,
1001
            // the cloned one will share the EventBuffer with the original one, while persistence would
1002
            // create 2 separate EventBuffers, which will then have a very stange behaviour
1003
        }
1004
        if (getMapContext() != null) {
1005
            if (layoutScale != null) {
1006
                getMapContext().setScaleView(layoutScale.longValue());
1007
            }
1008
            getMapContext().getViewPort().setEnvelope(envelope);
1009
            if (this.getLayoutContext() != null) {
1010
                this.getLayoutContext().setTocModel(getMapContext());
1011
            }
1012
        }
1013
        showGrid = state.getBoolean(SHOWGRID_FIELD);
1014
        grid = (IFFrame) state.get(GRID_FIELD);
1015
    }
1016

    
1017
    @Override
1018
    public void saveToState(PersistentState state) throws PersistenceException {
1019
        super.saveToState(state);
1020
        state.set(EXTENT_SYNC_FIELD, syncExtents);
1021
        state.set(LAYER_SYNC_FIELD, syncLayers);
1022
        state.set(QUALITY_FIELD, quality);
1023
        state.set(MAPUNITS_FIELD, mapUnits);
1024
        state.set(VIEW_FIELD, viewDocument);
1025
        state.set(HAS_TOC_FIELD, b_hasToc);
1026
        state.set(SCALE_TYPE_FIELD, scaleType.ordinal());
1027

    
1028
        if (getMapContext() != null
1029
                && getMapContext().getViewPort().getEnvelope() != null) {
1030
            if (scaleType == SCALE_TYPE.FIXED_SCALE) {
1031
                if (fixedScale == null) {
1032
                    fixedScale = new Double(getMapContext().getScaleView());
1033
                }
1034
                state.set(SCALE_FIELD, (double) fixedScale);
1035
            } else {
1036
                state.set(SCALE_FIELD, (double) getMapContext().getScaleView());
1037
            }
1038
            if (scaleType == SCALE_TYPE.FIXED_EXTENT) {
1039
                if (fixedExtent == null) {
1040
                    fixedExtent = getMapContext().getViewPort().getAdjustedEnvelope();
1041
                }
1042
                state.set(ENVELOPE_FIELD, fixedExtent);
1043
            } else {
1044
                state.set(ENVELOPE_FIELD, getMapContext().getViewPort()
1045
                        .getAdjustedEnvelope());
1046
            }
1047
        }
1048
        state.set(SHOWGRID_FIELD, showGrid);
1049
        state.set(GRID_FIELD, grid);
1050
        state.set(PERSIST_INDEPENDENT_FIELD, this.persistIndependent);
1051
    }
1052

    
1053
    @Override
1054
    public void setBoundBox(Rectangle2D r) {
1055
        super.setBoundBox(r);
1056
        if (this.getMapContext() != null && this.getLayoutContext() != null) {
1057
            AffineTransform at = this.getLayoutContext().getAT();
1058
            long scale = getMapContext().getScaleView();
1059
            getMapContext().getViewPort().setImageSize(
1060
                    new Dimension(
1061
                            (int) getBoundingBox(at).getWidth(),
1062
                            (int) getBoundingBox(at).getHeight()
1063
                    )
1064
            );
1065
            getMapContext().getViewPort().setDPI(getDrawPaperDPI());
1066
            if (getScaleType() == SCALE_TYPE.FIXED_SCALE) {
1067
                getMapContext().setScaleView((long) scale);
1068
            }
1069
            updateScaleCtrl();
1070
            refresh();
1071
        }
1072
    }
1073

    
1074
    /**
1075
     * Gets the rotation of the frame
1076
     *
1077
     * @return Rotation in degrees
1078
     */
1079
    public double getMapRotation() {
1080
        return 0;
1081
    }
1082

    
1083
    protected void invalidateLayout() {
1084
        b_validCache = false;
1085
        if (getLayoutContext() != null) {
1086
            getLayoutContext().notifAllObservers();
1087
        }
1088
        observers.notifyObservers(this,
1089
                new DefaultLayoutNotification(LayoutNotification.LAYOUT_REFRESH)
1090
        );
1091
    }
1092

    
1093
    protected void invalidateToc() {
1094
        if (getLayoutContext() != null) {
1095
            getLayoutContext().notifyTocUpdated(TocModelChangedNotification.Type.ITEM_UPDATED);
1096
        }
1097
    }
1098

    
1099
    protected void refreshToc() {
1100
        if (getLayoutContext() != null) {
1101
            getLayoutContext().notifyTocUpdated(TocModelChangedNotification.Type.MODEL_CHANGED);
1102
        }
1103
    }
1104

    
1105
    @Override
1106
    public void refresh() {
1107
        // force re-creating the cache on the next drawing cycle
1108
        b_validCache = false;
1109
    }
1110

    
1111
    protected void createListeners() {
1112
        this.removeAllListeners();
1113
        viewDocListener = new ViewDocListener(this);
1114
        ownMapContextListener = new OwnMapContextListener(this);
1115
//        logger.info("createListeners.viewDocListener "+ viewDocListener.getID());
1116
//        logger.info("createListeners.ownMapContextListener "+ ownMapContextListener.getID());
1117
    }
1118

    
1119
    protected void addAllListeners() {
1120
        if (viewDocListener == null && ownMapContextListener == null) {
1121
            this.createListeners();
1122
        }
1123
        if (getView() != null) {
1124
            if (syncLayers) {
1125
                getView().getMapContext().addLayerListener(viewDocListener);
1126
                getView().getMapContext().getLayers().addLayerCollectionListener(viewDocListener);
1127
                getView().getMapContext().addAtomicEventListener(viewDocListener);
1128
            }
1129
            if (getExtentSynced()) {
1130
                getView().getMapContext().getViewPort().addViewPortListener(viewDocListener);
1131
            }
1132
        }
1133
        if (getMapContext() != null) {
1134
            getMapContext().addLayerListener(ownMapContextListener);
1135
            getMapContext().getLayers().addLayerCollectionListener(ownMapContextListener);
1136
            getMapContext().getViewPort().addViewPortListener(ownMapContextListener);
1137
        }
1138
//        logger.info("addAllListeners.viewDocListener " + viewDocListener.getID());
1139
//        logger.info("addAllListeners.ownMapContextListener " + ownMapContextListener.getID());
1140

    
1141
    }
1142

    
1143
    protected void resetAllListeners() {
1144
        this.removeAllListeners();
1145
        this.addAllListeners();
1146
    }
1147

    
1148
    protected void removeAllListeners() {
1149
        removeOwnListeners();
1150
        removeViewListeners();
1151
    }
1152

    
1153
    protected void removeOwnListeners() {
1154
        if (this.ownMapContextListener == null) {
1155
            return;
1156
        }
1157
        if (this.mapContext == null) {
1158
            return;
1159
        }
1160
        this.mapContext.removeLayerListener(ownMapContextListener);
1161
        this.mapContext.getViewPort().removeViewPortListener(ownMapContextListener);
1162
        this.mapContext.getLayers().removeLayerCollectionListener(ownMapContextListener);
1163
//        logger.info("removeOwnListeners.ownMapContextListener " + ownMapContextListener.getID());
1164
    }
1165

    
1166
    protected void removeViewListeners() {
1167
        if (this.viewDocListener == null) {
1168
            return;
1169
        }
1170
        if (this.getView() == null) {
1171
            return;
1172
        }
1173
        MapContext mapContext = this.getView().getMapContext();
1174
        if (mapContext == null) {
1175
            return;
1176
        }
1177
        mapContext.removeLayerListener(viewDocListener);
1178
        mapContext.getViewPort().removeViewPortListener(viewDocListener);
1179
        mapContext.getLayers().removeLayerCollectionListener(viewDocListener);
1180
        mapContext.removeAtomicEventListener(viewDocListener);
1181
//        logger.info("removeViewListeners.viewDocListener " + viewDocListener.getID());
1182
    }
1183

    
1184
    @Override
1185
    public void dispose() {
1186
        this.removeAllListeners();
1187
        this.viewDocListener = null;
1188
        this.ownMapContextListener = null;
1189
        this.viewDocument = null;
1190
        this.mapContext = null;
1191
    }
1192

    
1193
    @Override
1194
    public void frameRemoved() {
1195
        this.removeAllListeners();
1196
        if (b_hasToc && getLayoutContext() != null) {
1197
            getLayoutContext().setTocModel(null);
1198
        }
1199
        m_image = null; // FIXME: we could instead move it to a LRU cache to keep the last N images
1200
    }
1201

    
1202
    @Override
1203
    public void frameAdded() {
1204
        addAllListeners();
1205
        setTocModel();
1206
        updateScaleCtrl();
1207
    }
1208

    
1209
    public void setHasToc(boolean hasToc) {
1210
        this.b_hasToc = hasToc;
1211
        setTocModel();
1212
    }
1213

    
1214
    protected void setTocModel() {
1215
        if (getLayoutContext() != null) {
1216
            if (b_hasToc && getMapContext() != null) {
1217
                getLayoutContext().setTocModel(getMapContext());
1218
            } else {
1219
                getLayoutContext().setTocModel(null);
1220
            }
1221
        }
1222
    }
1223

    
1224
    @Override
1225
    protected void doSetSelected(int selectedStatus) {
1226
        boolean oldSelectedStatus = isSelected();
1227
        super.doSetSelected(selectedStatus);
1228
        if (!oldSelectedStatus && isSelected()) { // changed from not selected to selected
1229
            setTocModel();
1230
            updateScaleCtrl();
1231
        }
1232
    }
1233

    
1234
    @Override
1235
    public boolean getLayerSynced() {
1236
        return syncLayers;
1237
    }
1238

    
1239
    @Override
1240
    public void setLayerSynced(boolean synced) {
1241
        syncLayers = synced;
1242
        resetAllListeners();
1243
    }
1244

    
1245
    @Override
1246
    public boolean getExtentSynced() {
1247
        return syncExtents;
1248
    }
1249

    
1250
    @Override
1251
    public void setExtentSynced(boolean synced) {
1252
        syncExtents = synced;
1253
        resetAllListeners();
1254
    }
1255

    
1256
    /**
1257
     * Returns true if the newEnvelope represents a pan operation on
1258
     * oldEnvelope, (both extents have the same height and width)
1259
     *
1260
     * @param oldEnvelope
1261
     * @param newEnvelope
1262
     * @return
1263
     */
1264
    public static boolean isPan(Envelope oldEnvelope, Envelope newEnvelope) {
1265
        if (oldEnvelope != null && newEnvelope != null) {
1266
            double toleranceX = 0.00000001 * Math.min(
1267
                    oldEnvelope.getLength(0), newEnvelope.getLength(0)
1268
            );
1269
            double toleranceY = 0.00000001 * Math.min(
1270
                    oldEnvelope.getLength(1), newEnvelope.getLength(1)
1271
            );
1272

    
1273
            // we consider it to be a pan if both lengths are equal
1274
            // (we use tolerance to avoid direct comparison of double values)
1275
            if ((Math.abs(oldEnvelope.getLength(0) - newEnvelope.getLength(0)) < toleranceX)
1276
                    && (Math.abs(oldEnvelope.getLength(1) - newEnvelope.getLength(1)) < toleranceY)) {
1277
                return true;
1278
            }
1279
        }
1280
        return false;
1281
    }
1282

    
1283
    /**
1284
     * Calculates the new extent for the FFrame. It is necessary to avoid scale
1285
     * changes when a pan is triggered from the view, as the different size
1286
     * factors from View/FFrameView introduces scale changes even for pans
1287
     *
1288
     * @return
1289
     */
1290
    protected Envelope calculateNewExtent() {
1291
        Envelope newEnvelope = getView().getMapContext().getViewPort().getEnvelope();
1292
        Envelope oldViewEnvelope = null;
1293
        try {
1294
            if (getView().getMapContext().getViewPort().getEnvelopes().hasPrevious()) {
1295
                Rectangle2D r = getView().getMapContext().getViewPort().getEnvelopes().getPrev();
1296
                oldViewEnvelope = GeometryLocator.getGeometryManager().createEnvelope(
1297
                        r.getMinX(), r.getMinY(), r.getMaxX(), r.getMaxY(), SUBTYPES.GEOM2D
1298
                );
1299
            }
1300
            if (isPan(oldViewEnvelope, newEnvelope)) {
1301
                Envelope envelope = getMapContext().getViewPort().getAdjustedEnvelope();
1302
                double shiftX = newEnvelope.getMinimum(0) - oldViewEnvelope.getMinimum(0);
1303
                double shiftY = newEnvelope.getMinimum(1) - oldViewEnvelope.getMinimum(1);
1304

    
1305
                double minX = envelope.getMinimum(0) + shiftX;
1306
                double minY = envelope.getMinimum(1) + shiftY;
1307
                double maxX = envelope.getMaximum(0) + shiftX;
1308
                double maxY = envelope.getMaximum(1) + shiftY;
1309

    
1310
                return GeometryLocator.getGeometryManager().createEnvelope(
1311
                        minX, minY, maxX, maxY, SUBTYPES.GEOM2D
1312
                );
1313
            }
1314
        } catch (Exception ex) {
1315
        }
1316
        return newEnvelope;
1317
    }
1318

    
1319
    protected boolean isDrawing() {
1320
        return this.b_drawing;
1321
    }
1322

    
1323
    protected boolean isUpdating() {
1324
        return this.b_updating;
1325
    }
1326

    
1327
    protected boolean skipFirstChangeExtent() {
1328
        if (this.b_frameInitialized) {
1329
            return false;
1330
        }
1331
        this.b_frameInitialized = true;
1332
        return true;
1333
    }
1334

    
1335
    protected boolean isSyncLayers() {
1336
        return this.syncLayers;
1337
    }
1338

    
1339
    protected void beginUpdate() {
1340
        this.b_updating = true;
1341
    }
1342

    
1343
    protected void endUpdate() {
1344
        this.b_updating = false;
1345
    }
1346

    
1347
    @Override
1348
    public SCALE_TYPE getScaleType() {
1349
        return this.scaleType;
1350
    }
1351

    
1352
    public Double getFixedScale() {
1353
        return this.fixedScale;
1354
    }
1355

    
1356
    public Envelope getFixedExtent() {
1357
        return this.fixedExtent;
1358
    }
1359

    
1360
    public void setExtent(Envelope extent) {
1361
        if (getScaleType() == SCALE_TYPE.NORMAL) {
1362
            getMapContext().getViewPort().setEnvelope(extent);
1363
        }
1364
    }
1365

    
1366
    @Override
1367
    public void setScaleType(SCALE_TYPE scaleType) {
1368
        this.scaleType = scaleType;
1369
    }
1370

    
1371
    public void setScaleType(SCALE_TYPE scaleType, double fixedScale) {
1372
        if (scaleType == SCALE_TYPE.FIXED_SCALE) {
1373
            this.scaleType = scaleType;
1374
            this.fixedScale = fixedScale;
1375
            setScale(fixedScale);
1376
        }
1377
    }
1378

    
1379
    public void setScaleType(SCALE_TYPE scaleType, Envelope fixedExtent) {
1380
        if (scaleType == SCALE_TYPE.FIXED_EXTENT) {
1381
            this.scaleType = scaleType;
1382
            this.fixedExtent = fixedExtent;
1383
            setNewEnvelope(fixedExtent);
1384
        }
1385
    }
1386

    
1387
    @Deprecated
1388
    @Override
1389
    public int getTypeScale() {
1390
        return AUTOMATICO;
1391
    }
1392

    
1393
    @Deprecated
1394
    @Override
1395
    public void setLinked(boolean b) {
1396
    }
1397

    
1398
    @Deprecated
1399
    @Override
1400
    public boolean getLinked() {
1401
        return false;
1402
    }
1403

    
1404
    @Override
1405
    public void windowActivated() {
1406
        if (isSelected()
1407
                && getLayoutContext().getSelectedFFrames(IFFrameUseFMap.class).length == 1) {
1408
            // update the scale control whenever the panel window gets activated,
1409
            // as the control instance is shared for all the windows
1410
            updateScaleCtrl();
1411
        }
1412

    
1413
    }
1414

    
1415
    @Override
1416
    public void windowClosed() {
1417
    }
1418

    
1419
    public void setPersistIndependent(boolean b) {
1420
        this.persistIndependent = b;
1421
    }
1422

    
1423
    public boolean isPersistIndependent() {
1424
        return this.persistIndependent;
1425
    }
1426
    
1427
    
1428
    private class ViewDocListener
1429
            implements ViewPortListener, LegendListener, LayerCollectionListener, AtomicEventListener {
1430

    
1431
        public ViewDocListener(FFrameView fview) {
1432

    
1433
        }
1434

    
1435
        @Override
1436
        public void extentChanged(ExtentEvent e) {
1437
            if (isUpdating()) {
1438
                return;
1439
            }
1440
            if (skipFirstChangeExtent()) {
1441
                return;
1442
            }
1443
            if (!getExtentSynced()) {
1444
                return;
1445
            }
1446
//                logger.info("viewdoc-mapcontext.extentChanged " + this.getID());
1447
            try {
1448
                beginUpdate();
1449
                if (getMapContext() != null) {
1450
                    if (null == getScaleType()) {
1451
                        getMapContext().getViewPort().setEnvelope(calculateNewExtent());
1452
                    } else {
1453
                        switch (getScaleType()) {
1454
                            case FIXED_EXTENT:
1455
                                getView().getMapContext().getViewPort().setEnvelope(getFixedExtent());
1456
                                break;
1457
                            case FIXED_SCALE:
1458
                                getMapContext().setScaleView(Math.round(getFixedScale()));
1459
                                getView().getMapContext().getViewPort().setEnvelope(getMapContext().getViewPort().getAdjustedEnvelope());
1460
                                break;
1461
                            default:
1462
                                getMapContext().getViewPort().setEnvelope(calculateNewExtent());
1463
                                break;
1464
                        }
1465
                    }
1466
                    updateScaleCtrl();
1467
                    invalidateLayout();
1468
                }
1469
            } finally {
1470
                endUpdate();
1471
            }
1472
        }
1473

    
1474
        @Override
1475
        public void backColorChanged(ColorEvent e) {
1476
            if (!isUpdating() && isSyncLayers()) {
1477
                if (getMapContext() != null) {
1478
                    beginUpdate();
1479
                    getMapContext().getViewPort().setBackColor(e.getNewColor());
1480
                    invalidateLayout();
1481
                    endUpdate();
1482
                }
1483
            }
1484
        }
1485

    
1486
        @Override
1487
        public void projectionChanged(ProjectionEvent e) {
1488
            if (!isUpdating() && getExtentSynced()) {
1489
                if (getMapContext() != null) {
1490
                    beginUpdate();
1491
                    getMapContext().getViewPort().setProjection(e.getNewProjection());
1492
                    invalidateLayout();
1493
                    // FIXME: force also a view redraw someway??
1494
                    endUpdate();
1495
                }
1496
            }
1497
        }
1498

    
1499
        public void conditionalRedraw() {
1500
            if (!isUpdating() && isSyncLayers()) {
1501
                beginUpdate();
1502
                invalidateLayout();
1503
                // the view should also receive the event and update automatically
1504
                endUpdate();
1505
            }
1506
        }
1507

    
1508
        @Override
1509
        public void legendChanged(LegendChangedEvent e) {
1510
            conditionalRedraw();
1511
            refreshToc();
1512
        }
1513

    
1514
        @Override
1515
        public void layerAdded(LayerCollectionEvent e) {
1516
            conditionalRedraw();
1517
            // no need to invalidate the toc as it is down by the OwnMapContextListener
1518
        }
1519

    
1520
        @Override
1521
        public void layerMoved(LayerPositionEvent e) {
1522
            conditionalRedraw();
1523
            // no need to invalidate the toc as it is down by the OwnMapContextListener
1524
        }
1525

    
1526
        @Override
1527
        public void layerRemoved(LayerCollectionEvent e) {
1528
            conditionalRedraw();
1529
            // no need to invalidate the toc as it is down by the OwnMapContextListener
1530
        }
1531

    
1532
        @Override
1533
        public void layerAdding(LayerCollectionEvent e)
1534
                throws CancelationException {
1535
            // nothing needed
1536
        }
1537

    
1538
        @Override
1539
        public void layerMoving(LayerPositionEvent e)
1540
                throws CancelationException {
1541
            // nothing needed
1542
        }
1543

    
1544
        @Override
1545
        public void layerRemoving(LayerCollectionEvent e)
1546
                throws CancelationException {
1547
            // nothing needed
1548
        }
1549

    
1550
        @Override
1551
        public void visibilityChanged(LayerCollectionEvent e)
1552
                throws CancelationException {
1553
            // AtomicEvent is catching visibility changes instead of this one,
1554
            // as this event is not being received. Maybe is only triggered if the
1555
            // visibility of the whole group changes??
1556
            conditionalRedraw();
1557
            invalidateToc();
1558
        }
1559

    
1560
        @Override
1561
        public void atomicEvent(AtomicEvent e) {
1562
            boolean layoutRedraw = false;
1563
            // not all the events require a fframeview redraw
1564
            for (int i = e.getLayerEvents().length - 1; i >= 0; i--) {
1565
                FMapEvent at = e.getEvent(i);
1566
                if (at instanceof LayerEvent) {
1567
                    if (at.getEventType() == LayerEvent.DRAW_VALUES_CHANGED) {
1568
                        layoutRedraw = true;
1569
                        break;
1570
                    }
1571
                }
1572
            }
1573
            if (layoutRedraw) {
1574
                conditionalRedraw();
1575
            }
1576
            invalidateToc();
1577
        }
1578
    }
1579

    
1580
    private class OwnMapContextListener
1581
            implements ViewPortListener, LegendListener, LayerCollectionListener {
1582

    
1583
        public OwnMapContextListener(FFrameView fview) {
1584

    
1585
        }
1586

    
1587
        @Override
1588
        public void extentChanged(ExtentEvent e) {
1589
            if (isDrawing()) {
1590
                return;
1591
            }
1592
            if (isUpdating()) {
1593
                return;
1594
            }
1595
            try {
1596
//                logger.info("layout-mapcontext.extentChanged "+this.getID());
1597
                beginUpdate();
1598
                if (getScaleType() == SCALE_TYPE.FIXED_EXTENT) {
1599
                    getMapContext().getViewPort().setEnvelope(getFixedExtent());
1600
                } else {
1601
                    Envelope newEnvelope;
1602
                    if (getScaleType() == SCALE_TYPE.FIXED_SCALE && getFixedScale() != null) {
1603
                        getMapContext().setScaleView(Math.round(getFixedScale()));
1604
                        newEnvelope = getMapContext().getViewPort().getAdjustedEnvelope();
1605
                    } else {
1606
                        newEnvelope = e.getNewExtent();
1607
                    }
1608
                    if (getView() != null) {
1609
                        if (getExtentSynced()) {
1610
                            getView().getMapContext().getViewPort().setEnvelope(newEnvelope);
1611
                        }
1612
                    }
1613
                }
1614
                updateScaleCtrl();
1615
                invalidateLayout();
1616
            } finally {
1617
                endUpdate();
1618
            }
1619
        }
1620

    
1621
        @Override
1622
        public void backColorChanged(ColorEvent e) {
1623
            if (!isUpdating()) {
1624
                if (getView() != null) {
1625
                    try {
1626
                        beginUpdate();
1627
                        if (getLayerSynced()) {
1628
                            getView().getMapContext().getViewPort().setBackColor(e.getNewColor());
1629
                        }
1630
                        invalidateLayout();
1631
                    } finally {
1632
                        endUpdate();
1633
                    }
1634
                }
1635
            }
1636
        }
1637

    
1638
        @Override
1639
        public void projectionChanged(ProjectionEvent e) {
1640
            if (!isUpdating() && getExtentSynced()) {
1641
                if (getView() != null) {
1642
                    try {
1643
                        beginUpdate();
1644
                        if (getLayerSynced()) {
1645
                            getView().getMapContext().getViewPort().setProjection(e.getNewProjection());
1646
                        }
1647
                        invalidateLayout();
1648
                        // FIXME: force also a view redraw someway??
1649
                    } finally {
1650
                        endUpdate();
1651
                    }
1652
                }
1653
            }
1654
        }
1655

    
1656
        public void conditionalRedraw() {
1657
            if (!isUpdating()) {
1658
                try {
1659
                    beginUpdate();
1660
                    invalidateLayout();
1661
                    // the view should also receive the event and update automatically
1662
                } finally {
1663
                    endUpdate();
1664
                }
1665
            }
1666
        }
1667

    
1668
        @Override
1669
        public void legendChanged(final LegendChangedEvent e) {
1670
            conditionalRedraw();
1671
            refreshToc();
1672
        }
1673

    
1674
        @Override
1675
        public void layerAdded(final LayerCollectionEvent e) {
1676
            // necessary to set an envelope when the first layer is added
1677
            if (!isUpdating() && getMapContext().getViewPort().getEnvelope() == null) {
1678
                try {
1679
                    beginUpdate();
1680
                    fullExtent();
1681

    
1682
                } catch (ReadException e1) {
1683
                } finally {
1684
                    endUpdate();
1685
                }
1686
            }
1687
            conditionalRedraw();
1688
            refreshToc();
1689
        }
1690

    
1691
        @Override
1692
        public void layerMoved(final LayerPositionEvent e) {
1693
            conditionalRedraw();
1694
            refreshToc();
1695
        }
1696

    
1697
        @Override
1698
        public void layerRemoved(final LayerCollectionEvent e) {
1699
            conditionalRedraw();
1700
            refreshToc();
1701
        }
1702

    
1703
        @Override
1704
        public void layerAdding(LayerCollectionEvent e)
1705
                throws CancelationException {
1706
            // nothing neededO
1707

    
1708
        }
1709

    
1710
        @Override
1711
        public void layerMoving(LayerPositionEvent e)
1712
                throws CancelationException {
1713
            // nothing needed
1714

    
1715
        }
1716

    
1717
        @Override
1718
        public void layerRemoving(LayerCollectionEvent e)
1719
                throws CancelationException {
1720
            // nothing needed
1721

    
1722
        }
1723

    
1724
        @Override
1725
        public void visibilityChanged(LayerCollectionEvent e)
1726
                throws CancelationException {
1727
            conditionalRedraw();
1728
        }
1729
    }
1730

    
1731
}