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

History | View | Annotate | Download (59.3 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.FLayer;
67
import org.gvsig.fmap.mapcontext.layers.LayerCollectionEvent;
68
import org.gvsig.fmap.mapcontext.layers.LayerCollectionListener;
69
import org.gvsig.fmap.mapcontext.layers.LayerEvent;
70
import org.gvsig.fmap.mapcontext.layers.LayerPositionEvent;
71
import org.gvsig.fmap.mapcontext.layers.vectorial.FLyrVect;
72
import org.gvsig.fmap.mapcontext.layers.vectorial.VectorLayer;
73
import org.gvsig.fmap.mapcontext.rendering.legend.events.LegendChangedEvent;
74
import org.gvsig.fmap.mapcontext.rendering.legend.events.listeners.LegendListener;
75
import org.gvsig.gui.beans.Messages;
76
import org.gvsig.tools.ToolsLocator;
77
import org.gvsig.tools.dynobject.DynStruct;
78
import org.gvsig.tools.persistence.PersistenceManager;
79
import org.gvsig.tools.persistence.PersistentState;
80
import org.gvsig.tools.persistence.exception.PersistenceException;
81
import org.slf4j.Logger;
82
import org.slf4j.LoggerFactory;
83

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

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

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

    
111
    private static final String QUALITY_FIELD = "quality";
112
    private static final String MAPUNITS_FIELD = "mapUnits";
113
    private static final String SCALE_FIELD = "scale";
114
    private static final String VIEW_FIELD = "view";
115
    private static final String ENVELOPE_FIELD = "envelope";
116
    private static final String SHOWGRID_FIELD = "showGrid";
117
    private static final String GRID_FIELD = "gridview";
118
    private static final String HAS_TOC_FIELD = "hasToc";
119
    private static final String LAYER_SYNC_FIELD = "layerSync";
120
    private static final String EXTENT_SYNC_FIELD = "extentSync";
121
    private static final String SCALE_TYPE_FIELD = "scaleType";
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

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

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

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

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

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

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

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

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

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

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

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

    
329
    }
330

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

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

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

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

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

    
403
    private void drawMessage(Graphics2D g, String msg) {
404

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

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

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

    
439
        double newWidth = oldEnv.getLength(0) * widthFactor;
440
        double newHeight = oldEnv.getLength(1) * heightFactor;
441

    
442
        double translateX = visiblefframeViewRect.x - fframeViewRect.x;
443
        double translateY = visiblefframeViewRect.y - fframeViewRect.y;
444
        double translateFactorX = translateX / fframeViewRect.width;
445
        double translateFactorY = translateY / fframeViewRect.height;
446

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

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

    
460
    }
461

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

    
469
        b_drawing = true;
470
        int drawWidth = (int) visibleRect.width;
471
        int drawHeight = (int) visibleRect.height;
472

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

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

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

    
495
        // paint the MapContext on m_image, if not already cached
496
        createImage(affineTransform, drawWidth, drawHeight, mapOrigin);
497

    
498
        //Draw the created image
499
        drawImage(g, m_image, visibleRect);
500

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

    
507
        scaleAnt = affineTransform.getScaleX();
508
        origin = mapOrigin;
509
        b_drawing = false;
510
    }
511

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

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

    
523
            viewPort.setDPI(getDrawPaperDPI());
524
            viewPort.setImageSize(new Dimension(width, height));
525

    
526
            m_image = new BufferedImage(
527
                    width,
528
                    height,
529
                    BufferedImage.TYPE_INT_ARGB
530
            );
531

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

    
543
    }
544

    
545
    protected void drawImage(Graphics2D g, BufferedImage image,
546
            Rectangle2D.Double visibleRectangle) {
547

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

    
561
    protected void preDraw(Graphics2D g, Rectangle2D.Double fframeViewRect, Rectangle2D.Double visibleRect) {
562
        originalGraphicsAT = (AffineTransform) g.getTransform().clone();
563

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

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

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

    
600
    }
601

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

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

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

    
631
    protected void print(Graphics2D g, AffineTransform at, PrintAttributes printAttributes) {
632
        Rectangle2D.Double layoutRectangle = getBoundingBox(at);
633

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

    
640
        Point2D old_offset = viewPort.getOffset();
641
        Dimension old_imgsize = viewPort.getImageSize();
642
        double oldDpi = viewPort.getDPI();
643

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

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

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

    
673
        // Restore offset, imgsize
674
        viewPort.setOffset(old_offset);
675
        viewPort.setImageSize(old_imgsize);
676
        viewPort.setDPI(oldDpi);
677

    
678
    }
679

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

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

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

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

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

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

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

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

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

    
766
    @Override
767
    public String getName() {
768
        return PERSISTENCE_DEFINITION_NAME;
769
    }
770

    
771
    public boolean compare(Object arg0) {
772
        if (!(arg0 instanceof FFrameView)) {
773
            return false;
774
        }
775

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

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

    
789
        if (!this.toString().equals(((FFrameView) arg0).toString())) {
790
            return false;
791
        }
792

    
793
        if (this.getMapContext() != null
794
                && !this.getMapContext()
795
                        .equals(((FFrameView) arg0).getMapContext())) {
796
            return false;
797
        }
798

    
799
        if (this.getRotation() != ((FFrameView) arg0).getRotation()) {
800
            return false;
801
        }
802
        return true;
803
    }
804

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

    
832
    @Override
833
    public void fullExtent() throws ReadException {
834
        setNewEnvelope(getMapContext().getFullEnvelope());
835
    }
836

    
837
    @Override
838
    public void setPointsToZoom(Point2D px1, Point2D px2) {
839
        p1 = px1;
840
        p2 = px2;
841
    }
842

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

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

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

    
870
    public void setGrid(IFFrame grid) {
871
        this.grid = grid;
872
        this.grid.setRotation(this.getRotation());
873
    }
874

    
875
    public IFFrame getGrid() {
876
        return this.grid;
877
    }
878

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

    
887
    public void showGrid(boolean b) {
888
        showGrid = b;
889
    }
890

    
891
    public boolean isShowGrid() {
892
        return showGrid;
893
    }
894

    
895
    @Override
896
    public void refreshOriginalExtent() {
897
    }
898

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

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

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

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

    
1004
    @Override
1005
    public void saveToState(PersistentState state) throws PersistenceException {
1006
        super.saveToState(state);
1007
        state.set(EXTENT_SYNC_FIELD, syncExtents);
1008
        state.set(LAYER_SYNC_FIELD, syncLayers);
1009
        state.set(QUALITY_FIELD, quality);
1010
        state.set(MAPUNITS_FIELD, mapUnits);
1011
        state.set(VIEW_FIELD, viewDocument);
1012
        state.set(HAS_TOC_FIELD, b_hasToc);
1013
        state.set(SCALE_TYPE_FIELD, scaleType.ordinal());
1014

    
1015
        if (getMapContext() != null
1016
                && getMapContext().getViewPort().getEnvelope() != null) {
1017
            if (scaleType == SCALE_TYPE.FIXED_SCALE) {
1018
                if (fixedScale == null) {
1019
                    fixedScale = new Double(getMapContext().getScaleView());
1020
                }
1021
                state.set(SCALE_FIELD, (double) fixedScale);
1022
            } else {
1023
                state.set(SCALE_FIELD, (double) getMapContext().getScaleView());
1024
            }
1025
            if (scaleType == SCALE_TYPE.FIXED_EXTENT) {
1026
                if (fixedExtent == null) {
1027
                    fixedExtent = getMapContext().getViewPort().getAdjustedEnvelope();
1028
                }
1029
                state.set(ENVELOPE_FIELD, fixedExtent);
1030
            } else {
1031
                state.set(ENVELOPE_FIELD, getMapContext().getViewPort()
1032
                        .getAdjustedEnvelope());
1033
            }
1034
        }
1035
        state.set(SHOWGRID_FIELD, showGrid);
1036
        state.set(GRID_FIELD, grid);
1037
    }
1038

    
1039
    @Override
1040
    public void setBoundBox(Rectangle2D r) {
1041
        super.setBoundBox(r);
1042
        if (this.getMapContext() != null && this.getLayoutContext() != null) {
1043
            AffineTransform at = this.getLayoutContext().getAT();
1044
            long scale = getMapContext().getScaleView();
1045
            getMapContext().getViewPort().setImageSize(
1046
                    new Dimension(
1047
                            (int) getBoundingBox(at).getWidth(),
1048
                            (int) getBoundingBox(at).getHeight()
1049
                    )
1050
            );
1051
            getMapContext().getViewPort().setDPI(getDrawPaperDPI());
1052
            if (getScaleType() == SCALE_TYPE.FIXED_SCALE) {
1053
                getMapContext().setScaleView((long) scale);
1054
            }
1055
            updateScaleCtrl();
1056
            refresh();
1057
        }
1058
    }
1059

    
1060
    /**
1061
     * Gets the rotation of the frame
1062
     *
1063
     * @return Rotation in degrees
1064
     */
1065
    public double getMapRotation() {
1066
        return 0;
1067
    }
1068

    
1069
    protected void invalidateLayout() {
1070
        b_validCache = false;
1071
        if (getLayoutContext() != null) {
1072
            getLayoutContext().notifAllObservers();
1073
        }
1074
        observers.notifyObservers(this,
1075
                new DefaultLayoutNotification(LayoutNotification.LAYOUT_REFRESH)
1076
        );
1077
    }
1078

    
1079
    protected void invalidateToc() {
1080
        if (getLayoutContext() != null) {
1081
            getLayoutContext().notifyTocUpdated(TocModelChangedNotification.Type.ITEM_UPDATED);
1082
        }
1083
    }
1084

    
1085
    protected void refreshToc() {
1086
        if (getLayoutContext() != null) {
1087
            getLayoutContext().notifyTocUpdated(TocModelChangedNotification.Type.MODEL_CHANGED);
1088
        }
1089
    }
1090

    
1091
    @Override
1092
    public void refresh() {
1093
        // force re-creating the cache on the next drawing cycle
1094
        b_validCache = false;
1095
    }
1096

    
1097
    protected void createListeners() {
1098
        this.removeAllListeners();
1099
        viewDocListener = new ViewDocListener(this);
1100
        ownMapContextListener = new OwnMapContextListener(this);
1101
//        logger.info("createListeners.viewDocListener "+ viewDocListener.getID());
1102
//        logger.info("createListeners.ownMapContextListener "+ ownMapContextListener.getID());
1103
    }
1104

    
1105
    protected void addAllListeners() {
1106
        if (viewDocListener == null && ownMapContextListener == null) {
1107
            this.createListeners();
1108
        }
1109
        if (getView() != null) {
1110
            if (syncLayers) {
1111
                getView().getMapContext().addLayerListener(viewDocListener);
1112
                getView().getMapContext().getLayers().addLayerCollectionListener(viewDocListener);
1113
                getView().getMapContext().addAtomicEventListener(viewDocListener);
1114
            }
1115
            if (getExtentSynced()) {
1116
                getView().getMapContext().getViewPort().addViewPortListener(viewDocListener);
1117
            }
1118
        }
1119
        if (getMapContext() != null) {
1120
            getMapContext().addLayerListener(ownMapContextListener);
1121
            getMapContext().getLayers().addLayerCollectionListener(ownMapContextListener);
1122
            getMapContext().getViewPort().addViewPortListener(ownMapContextListener);
1123
        }
1124
//        logger.info("addAllListeners.viewDocListener " + viewDocListener.getID());
1125
//        logger.info("addAllListeners.ownMapContextListener " + ownMapContextListener.getID());
1126

    
1127
    }
1128

    
1129
    protected void resetAllListeners() {
1130
        this.removeAllListeners();
1131
        this.addAllListeners();
1132
    }
1133

    
1134
    protected void removeAllListeners() {
1135
        removeOwnListeners();
1136
        removeViewListeners();
1137
    }
1138

    
1139
    protected void removeOwnListeners() {
1140
        if (this.ownMapContextListener == null) {
1141
            return;
1142
        }
1143
        if (this.mapContext == null) {
1144
            return;
1145
        }
1146
        this.mapContext.removeLayerListener(ownMapContextListener);
1147
        this.mapContext.getViewPort().removeViewPortListener(ownMapContextListener);
1148
        this.mapContext.getLayers().removeLayerCollectionListener(ownMapContextListener);
1149
//        logger.info("removeOwnListeners.ownMapContextListener " + ownMapContextListener.getID());
1150
    }
1151

    
1152
    protected void removeViewListeners() {
1153
        if (this.viewDocListener == null) {
1154
            return;
1155
        }
1156
        if (this.getView() == null) {
1157
            return;
1158
        }
1159
        MapContext mapContext = this.getView().getMapContext();
1160
        if (mapContext == null) {
1161
            return;
1162
        }
1163
        mapContext.removeLayerListener(viewDocListener);
1164
        mapContext.getViewPort().removeViewPortListener(viewDocListener);
1165
        mapContext.getLayers().removeLayerCollectionListener(viewDocListener);
1166
        mapContext.removeAtomicEventListener(viewDocListener);
1167
//        logger.info("removeViewListeners.viewDocListener " + viewDocListener.getID());
1168
    }
1169

    
1170
    @Override
1171
    public void dispose() {
1172
        this.removeAllListeners();
1173
        this.viewDocListener = null;
1174
        this.ownMapContextListener = null;
1175
        this.viewDocument = null;
1176
        this.mapContext = null;
1177
    }
1178

    
1179
    @Override
1180
    public void frameRemoved() {
1181
        this.removeAllListeners();
1182
        if (b_hasToc && getLayoutContext() != null) {
1183
            getLayoutContext().setTocModel(null);
1184
        }
1185
        m_image = null; // FIXME: we could instead move it to a LRU cache to keep the last N images
1186
    }
1187

    
1188
    @Override
1189
    public void frameAdded() {
1190
        addAllListeners();
1191
        setTocModel();
1192
        updateScaleCtrl();
1193
    }
1194

    
1195
    public void setHasToc(boolean hasToc) {
1196
        this.b_hasToc = hasToc;
1197
        setTocModel();
1198
    }
1199

    
1200
    protected void setTocModel() {
1201
        if (getLayoutContext() != null) {
1202
            if (b_hasToc && getMapContext() != null) {
1203
                getLayoutContext().setTocModel(getMapContext());
1204
            } else {
1205
                getLayoutContext().setTocModel(null);
1206
            }
1207
        }
1208
    }
1209

    
1210
    @Override
1211
    protected void doSetSelected(int selectedStatus) {
1212
        boolean oldSelectedStatus = isSelected();
1213
        super.doSetSelected(selectedStatus);
1214
        if (!oldSelectedStatus && isSelected()) { // changed from not selected to selected
1215
            setTocModel();
1216
            updateScaleCtrl();
1217
        }
1218
    }
1219

    
1220
    @Override
1221
    public boolean getLayerSynced() {
1222
        return syncLayers;
1223
    }
1224

    
1225
    @Override
1226
    public void setLayerSynced(boolean synced) {
1227
        syncLayers = synced;
1228
        resetAllListeners();
1229
    }
1230

    
1231
    @Override
1232
    public boolean getExtentSynced() {
1233
        return syncExtents;
1234
    }
1235

    
1236
    @Override
1237
    public void setExtentSynced(boolean synced) {
1238
        syncExtents = synced;
1239
        resetAllListeners();
1240
    }
1241

    
1242
    /**
1243
     * Returns true if the newEnvelope represents a pan operation on
1244
     * oldEnvelope, (both extents have the same height and width)
1245
     *
1246
     * @param oldEnvelope
1247
     * @param newEnvelope
1248
     * @return
1249
     */
1250
    public static boolean isPan(Envelope oldEnvelope, Envelope newEnvelope) {
1251
        if (oldEnvelope != null && newEnvelope != null) {
1252
            double toleranceX = 0.00000001 * Math.min(
1253
                    oldEnvelope.getLength(0), newEnvelope.getLength(0)
1254
            );
1255
            double toleranceY = 0.00000001 * Math.min(
1256
                    oldEnvelope.getLength(1), newEnvelope.getLength(1)
1257
            );
1258

    
1259
            // we consider it to be a pan if both lengths are equal
1260
            // (we use tolerance to avoid direct comparison of double values)
1261
            if (((oldEnvelope.getLength(0) - newEnvelope.getLength(0)) < toleranceX)
1262
                    && ((oldEnvelope.getLength(1) - newEnvelope.getLength(1)) < toleranceY)) {
1263
                return true;
1264
            }
1265
        }
1266
        return false;
1267
    }
1268

    
1269
    /**
1270
     * Calculates the new extent for the FFrame. It is necessary to avoid scale
1271
     * changes when a pan is triggered from the view, as the different size
1272
     * factors from View/FFrameView introduces scale changes even for pans
1273
     *
1274
     * @return
1275
     */
1276
    protected Envelope calculateNewExtent() {
1277
        Envelope newEnvelope = getView().getMapContext().getViewPort().getEnvelope();
1278
        Envelope oldViewEnvelope = null;
1279
        try {
1280
            if (getView().getMapContext().getViewPort().getEnvelopes().hasPrevious()) {
1281
                Rectangle2D r = getView().getMapContext().getViewPort().getEnvelopes().getPrev();
1282
                oldViewEnvelope = GeometryLocator.getGeometryManager().createEnvelope(
1283
                        r.getMinX(), r.getMinY(), r.getMaxX(), r.getMaxY(), SUBTYPES.GEOM2D
1284
                );
1285
            }
1286
            if (isPan(oldViewEnvelope, newEnvelope)) {
1287
                Envelope envelope = getMapContext().getViewPort().getAdjustedEnvelope();
1288
                double shiftX = newEnvelope.getMinimum(0) - oldViewEnvelope.getMinimum(0);
1289
                double shiftY = newEnvelope.getMinimum(1) - oldViewEnvelope.getMinimum(1);
1290

    
1291
                double minX = envelope.getMinimum(0) + shiftX;
1292
                double minY = envelope.getMinimum(1) + shiftY;
1293
                double maxX = envelope.getMaximum(0) + shiftX;
1294
                double maxY = envelope.getMaximum(1) + shiftY;
1295

    
1296
                return GeometryLocator.getGeometryManager().createEnvelope(
1297
                        minX, minY, maxX, maxY, SUBTYPES.GEOM2D
1298
                );
1299
            }
1300
        } catch (Exception ex) {
1301
        }
1302
        return newEnvelope;
1303
    }
1304

    
1305
    protected boolean isDrawing() {
1306
        return this.b_drawing;
1307
    }
1308

    
1309
    protected boolean isUpdating() {
1310
        return this.b_updating;
1311
    }
1312

    
1313
    protected boolean skipFirstChangeExtent() {
1314
        if (this.b_frameInitialized) {
1315
            return false;
1316
        }
1317
        this.b_frameInitialized = true;
1318
        return true;
1319
    }
1320

    
1321
    protected boolean isSyncLayers() {
1322
        return this.syncLayers;
1323
    }
1324

    
1325
    protected void beginUpdate() {
1326
        this.b_updating = true;
1327
    }
1328

    
1329
    protected void endUpdate() {
1330
        this.b_updating = false;
1331
    }
1332

    
1333
    @Override
1334
    public SCALE_TYPE getScaleType() {
1335
        return this.scaleType;
1336
    }
1337

    
1338
    public Double getFixedScale() {
1339
        return this.fixedScale;
1340
    }
1341

    
1342
    public Envelope getFixedExtent() {
1343
        return this.fixedExtent;
1344
    }
1345

    
1346
    public void setExtent(Envelope extent) {
1347
        if (getScaleType() == SCALE_TYPE.NORMAL) {
1348
            getMapContext().getViewPort().setEnvelope(extent);
1349
        }
1350
    }
1351

    
1352
    @Override
1353
    public void setScaleType(SCALE_TYPE scaleType) {
1354
        this.scaleType = scaleType;
1355
    }
1356

    
1357
    public void setScaleType(SCALE_TYPE scaleType, double fixedScale) {
1358
        if (scaleType == SCALE_TYPE.FIXED_SCALE) {
1359
            this.scaleType = scaleType;
1360
            this.fixedScale = fixedScale;
1361
            setScale(fixedScale);
1362
        }
1363
    }
1364

    
1365
    public void setScaleType(SCALE_TYPE scaleType, Envelope fixedExtent) {
1366
        if (scaleType == SCALE_TYPE.FIXED_EXTENT) {
1367
            this.scaleType = scaleType;
1368
            this.fixedExtent = fixedExtent;
1369
            setNewEnvelope(fixedExtent);
1370
        }
1371
    }
1372

    
1373
    @Deprecated
1374
    @Override
1375
    public int getTypeScale() {
1376
        return AUTOMATICO;
1377
    }
1378

    
1379
    @Deprecated
1380
    @Override
1381
    public void setLinked(boolean b) {
1382
    }
1383

    
1384
    @Deprecated
1385
    @Override
1386
    public boolean getLinked() {
1387
        return false;
1388
    }
1389

    
1390
    @Override
1391
    public void windowActivated() {
1392
        if (isSelected()
1393
                && getLayoutContext().getSelectedFFrames(IFFrameUseFMap.class).length == 1) {
1394
            // update the scale control whenever the panel window gets activated,
1395
            // as the control instance is shared for all the windows
1396
            updateScaleCtrl();
1397
        }
1398

    
1399
    }
1400

    
1401
    @Override
1402
    public void windowClosed() {
1403
    }
1404

    
1405
    private class ViewDocListener
1406
            implements ViewPortListener, LegendListener, LayerCollectionListener, AtomicEventListener {
1407

    
1408
        public ViewDocListener(FFrameView fview) {
1409

    
1410
        }
1411

    
1412
        @Override
1413
        public void extentChanged(ExtentEvent e) {
1414
            if (isUpdating()) {
1415
                return;
1416
            }
1417
            if (skipFirstChangeExtent()) {
1418
                return;
1419
            }
1420
            if (!getExtentSynced()) {
1421
                return;
1422
            }
1423
//                logger.info("viewdoc-mapcontext.extentChanged " + this.getID());
1424
            try {
1425
                beginUpdate();
1426
                if (getMapContext() != null) {
1427
                    if (null == getScaleType()) {
1428
                        getMapContext().getViewPort().setEnvelope(calculateNewExtent());
1429
                    } else {
1430
                        switch (getScaleType()) {
1431
                            case FIXED_EXTENT:
1432
                                getView().getMapContext().getViewPort().setEnvelope(getFixedExtent());
1433
                                break;
1434
                            case FIXED_SCALE:
1435
                                getMapContext().setScaleView(Math.round(getFixedScale()));
1436
                                getView().getMapContext().getViewPort().setEnvelope(getMapContext().getViewPort().getAdjustedEnvelope());
1437
                                break;
1438
                            default:
1439
                                getMapContext().getViewPort().setEnvelope(calculateNewExtent());
1440
                                break;
1441
                        }
1442
                    }
1443
                    updateScaleCtrl();
1444
                    invalidateLayout();
1445
                }
1446
            } finally {
1447
                endUpdate();
1448
            }
1449
        }
1450

    
1451
        @Override
1452
        public void backColorChanged(ColorEvent e) {
1453
            if (!isUpdating() && isSyncLayers()) {
1454
                if (getMapContext() != null) {
1455
                    beginUpdate();
1456
                    getMapContext().getViewPort().setBackColor(e.getNewColor());
1457
                    invalidateLayout();
1458
                    endUpdate();
1459
                }
1460
            }
1461
        }
1462

    
1463
        @Override
1464
        public void projectionChanged(ProjectionEvent e) {
1465
            if (!isUpdating() && getExtentSynced()) {
1466
                if (getMapContext() != null) {
1467
                    beginUpdate();
1468
                    getMapContext().getViewPort().setProjection(e.getNewProjection());
1469
                    invalidateLayout();
1470
                    // FIXME: force also a view redraw someway??
1471
                    endUpdate();
1472
                }
1473
            }
1474
        }
1475

    
1476
        public void conditionalRedraw() {
1477
            if (!isUpdating() && isSyncLayers()) {
1478
                beginUpdate();
1479
                invalidateLayout();
1480
                // the view should also receive the event and update automatically
1481
                endUpdate();
1482
            }
1483
        }
1484

    
1485
        @Override
1486
        public void legendChanged(LegendChangedEvent e) {
1487
            conditionalRedraw();
1488
            refreshToc();
1489
        }
1490

    
1491
        @Override
1492
        public void layerAdded(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 layerMoved(LayerPositionEvent e) {
1499
            conditionalRedraw();
1500
            // no need to invalidate the toc as it is down by the OwnMapContextListener
1501
        }
1502

    
1503
        @Override
1504
        public void layerRemoved(LayerCollectionEvent e) {
1505
            conditionalRedraw();
1506
            // no need to invalidate the toc as it is down by the OwnMapContextListener
1507
        }
1508

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

    
1515
        @Override
1516
        public void layerMoving(LayerPositionEvent e)
1517
                throws CancelationException {
1518
            // nothing needed
1519
        }
1520

    
1521
        @Override
1522
        public void layerRemoving(LayerCollectionEvent e)
1523
                throws CancelationException {
1524
            // nothing needed
1525
        }
1526

    
1527
        @Override
1528
        public void visibilityChanged(LayerCollectionEvent e)
1529
                throws CancelationException {
1530
            // AtomicEvent is catching visibility changes instead of this one,
1531
            // as this event is not being received. Maybe is only triggered if the
1532
            // visibility of the whole group changes??
1533
            conditionalRedraw();
1534
            invalidateToc();
1535
        }
1536

    
1537
        @Override
1538
        public void atomicEvent(AtomicEvent e) {
1539
            boolean layoutRedraw = false;
1540
            // not all the events require a fframeview redraw
1541
            for (int i = e.getLayerEvents().length - 1; i >= 0; i--) {
1542
                FMapEvent at = e.getEvent(i);
1543
                if (at instanceof LayerEvent) {
1544
                    if (at.getEventType() == LayerEvent.DRAW_VALUES_CHANGED) {
1545
                        layoutRedraw = true;
1546
                        break;
1547
                    }
1548
                }
1549
            }
1550
            if (layoutRedraw) {
1551
                conditionalRedraw();
1552
            }
1553
            invalidateToc();
1554
        }
1555
    }
1556

    
1557
    private class OwnMapContextListener
1558
            implements ViewPortListener, LegendListener, LayerCollectionListener {
1559

    
1560
        public OwnMapContextListener(FFrameView fview) {
1561

    
1562
        }
1563

    
1564
        @Override
1565
        public void extentChanged(ExtentEvent e) {
1566
            if (isDrawing()) {
1567
                return;
1568
            }
1569
            if (isUpdating()) {
1570
                return;
1571
            }
1572
            try {
1573
//                logger.info("layout-mapcontext.extentChanged "+this.getID());
1574
                beginUpdate();
1575
                if (getScaleType() == SCALE_TYPE.FIXED_EXTENT) {
1576
                    getMapContext().getViewPort().setEnvelope(getFixedExtent());
1577
                } else {
1578
                    Envelope newEnvelope;
1579
                    if (getScaleType() == SCALE_TYPE.FIXED_SCALE && getFixedScale() != null) {
1580
                        getMapContext().setScaleView(Math.round(getFixedScale()));
1581
                        newEnvelope = getMapContext().getViewPort().getAdjustedEnvelope();
1582
                    } else {
1583
                        newEnvelope = e.getNewExtent();
1584
                    }
1585
                    if (getView() != null) {
1586
                        if (getExtentSynced()) {
1587
                            getView().getMapContext().getViewPort().setEnvelope(newEnvelope);
1588
                        }
1589
                    }
1590
                }
1591
                updateScaleCtrl();
1592
                invalidateLayout();
1593
            } finally {
1594
                endUpdate();
1595
            }
1596
        }
1597

    
1598
        @Override
1599
        public void backColorChanged(ColorEvent e) {
1600
            if (!isUpdating()) {
1601
                if (getView() != null) {
1602
                    try {
1603
                        beginUpdate();
1604
                        if (getLayerSynced()) {
1605
                            getView().getMapContext().getViewPort().setBackColor(e.getNewColor());
1606
                        }
1607
                        invalidateLayout();
1608
                    } finally {
1609
                        endUpdate();
1610
                    }
1611
                }
1612
            }
1613
        }
1614

    
1615
        @Override
1616
        public void projectionChanged(ProjectionEvent e) {
1617
            if (!isUpdating() && getExtentSynced()) {
1618
                if (getView() != null) {
1619
                    try {
1620
                        beginUpdate();
1621
                        if (getLayerSynced()) {
1622
                            getView().getMapContext().getViewPort().setProjection(e.getNewProjection());
1623
                        }
1624
                        invalidateLayout();
1625
                        // FIXME: force also a view redraw someway??
1626
                    } finally {
1627
                        endUpdate();
1628
                    }
1629
                }
1630
            }
1631
        }
1632

    
1633
        public void conditionalRedraw() {
1634
            if (!isUpdating()) {
1635
                try {
1636
                    beginUpdate();
1637
                    invalidateLayout();
1638
                    // the view should also receive the event and update automatically
1639
                } finally {
1640
                    endUpdate();
1641
                }
1642
            }
1643
        }
1644

    
1645
        @Override
1646
        public void legendChanged(final LegendChangedEvent e) {
1647
            conditionalRedraw();
1648
            refreshToc();
1649
        }
1650

    
1651
        @Override
1652
        public void layerAdded(final LayerCollectionEvent e) {
1653
            // necessary to set an envelope when the first layer is added
1654
            if (!isUpdating() && getMapContext().getViewPort().getEnvelope() == null) {
1655
                try {
1656
                    beginUpdate();
1657
                    fullExtent();
1658

    
1659
                } catch (ReadException e1) {
1660
                } finally {
1661
                    endUpdate();
1662
                }
1663
            }
1664
            conditionalRedraw();
1665
            refreshToc();
1666
        }
1667

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

    
1674
        @Override
1675
        public void layerRemoved(final LayerCollectionEvent e) {
1676
            conditionalRedraw();
1677
            refreshToc();
1678
        }
1679

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

    
1685
        }
1686

    
1687
        @Override
1688
        public void layerMoving(LayerPositionEvent e)
1689
                throws CancelationException {
1690
            // nothing needed
1691

    
1692
        }
1693

    
1694
        @Override
1695
        public void layerRemoving(LayerCollectionEvent e)
1696
                throws CancelationException {
1697
            // nothing needed
1698

    
1699
        }
1700

    
1701
        @Override
1702
        public void visibilityChanged(LayerCollectionEvent e)
1703
                throws CancelationException {
1704
            conditionalRedraw();
1705
        }
1706
    }
1707

    
1708
}