sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1884563 [3/4] - in /sis/ip-review: ./ rev/01091/ rev/01094/ rev/01096/ rev/01097/ rev/01188/ rev/05052/ rev/05999/ rev/06057/ rev/06067/ rev/06068/ rev/06410/ rev/10796/ rev/11086/ rev/12288/ rev/17452/ rev/20874/ rev/20883/ rev/24476/
Date Thu, 17 Dec 2020 17:18:07 GMT
Added: sis/ip-review/rev/01188/ZoomPane.xhtml
URL: http://svn.apache.org/viewvc/sis/ip-review/rev/01188/ZoomPane.xhtml?rev=1884563&view=auto
==============================================================================
--- sis/ip-review/rev/01188/ZoomPane.xhtml (added)
+++ sis/ip-review/rev/01188/ZoomPane.xhtml Thu Dec 17 17:18:06 2020
@@ -0,0 +1,1796 @@
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml">
+  <head>
+    <meta charset="UTF-8"/>
+    <title>ZoomPane changes for revisions 1094:1188</title>
+    <style type="text/css" media="all">
+      @import url("../../reports.css");
+    </style>
+  </head>
+  <body>
+    <div>
+      <h1>ZoomPane changes for revisions 1094:1188</h1>
+
+      <p>Translation of comments from French to English.
+      The <code>LINDA UP TO HERE</code> comment suggests that translations of previous lines (not shown in this commit)
+      were also done by someone else than the committer. This commit contains also some code reformatting,
+      but the reformatting part does not apply to Apache SIS (code in SIS has been reformatted again).</p>
+
+      <p>The translations are not works that we can port to Apache SIS as-is.
+      However the original work – the sentences in French – can be ported to Apache SIS without problems.
+      In the code committed in Apache repository, we retranslated some sentences from the original French sentences.
+      Some other sentences have only been reworded. I did that when the sentence was similar to what I would have written
+      if I was redoing the translation from French anyway, but with sentences written in a more "javadoc-like" way and with
+      some terms replaced by more standard words used in Java (e.g. "redefine" → "override", "display" → "show", <i>etc.</i>).
+      The sentences that I did not changed are sentences that I would have written in approximately the same way anyway
+      if I was redoing the translation from French.</p>
+
+      <p>As of December 2020, this code is not committed in the main Apache SIS branch.
+      Instead we create a separated branch called "visual test" for now, with no intent to bring it back to main branch.
+      The reason is that we do not plan to provide Swing application in Apache SIS; instead we are developing a JavaFX application.
+      This Swing component is ported only as a debugging tool.</p>
+
+      <p>In this process I have learn a new English word: <cite>"whilst"</cite>.</p>
+
+<blockquote><code>svn diff --extensions "--unified --ignore-space-change --ignore-all-space --ignore-eol-style" -r1094:1188 https://svn.osgeo.org/geotools/trunk/modules/extension/widgets-swing/src/main/java/org/geotools/gui/swing/ZoomPane.java</code></blockquote>
+<table class="changes">
+<tr><th>Revision 1094</th><th>Revision 1188</th></tr>
+<tr><td><pre>* by the user through the scrollbars will be translated by calls to
+* {@link #transform}.&lt;/p&gt;
+*
+* @version 1.0
+* @author Martin Desruisseaux
+*/</pre></td>
+<td><pre>* by the user through the scrollbars will be translated by calls to
+* {@link #transform}.&lt;/p&gt;
+*
+<span class="add">* $Id: ZoomPane.java,v 1.4 2002/07/22 09:24:18 jmacgill Exp $</span>
+* @version 1.0
+* @author Martin Desruisseaux
+*/</pre></td></tr>
+<tr><td><pre> * @version 1.0
+ * @author Martin Desruisseaux
+ */
+<span class="del">private final class Listeners extends MouseAdapter implements MouseWheelListener, ComponentListener, Serializable</span>
+{
+    public void mouseWheelMoved (final MouseWheelEvent event) {ZoomPane.this.mouseWheelMoved (event);}
+    public void mousePressed    (final MouseEvent      event) {ZoomPane.this.mayShowPopupMenu(event);}</pre></td>
+<td><pre> * @version 1.0
+ * @author Martin Desruisseaux
+ */
+<span class="add">private final class Listeners extends MouseAdapter implements MouseWheelListener,</span>
+<span class="add">                                                              ComponentListener, Serializable</span>
+{
+    public void mouseWheelMoved (final MouseWheelEvent event) {ZoomPane.this.mouseWheelMoved (event);}
+    public void mousePressed    (final MouseEvent      event) {ZoomPane.this.mayShowPopupMenu(event);}</pre></td></tr>
+<tr><td><pre>        }
+        double m=amount;
+        if (button || (event.getModifiers() &amp; ActionEvent.SHIFT_MASK)!=0) {
+<span class="del">            if ((actionType &amp; UNIFORM_SCALE)!=0) m = (m&gt;=1) ? 2.0 : 0.5;</span>
+<span class="del">            else                                 m*= ENHANCEMENT_FACTOR;</span>
+        }
+        transform(actionType &amp; type, m, point);
+    }
+};</pre></td>
+<td><pre>        }
+        double m=amount;
+        if (button || (event.getModifiers() &amp; ActionEvent.SHIFT_MASK)!=0) {
+<span class="add">            if ((actionType &amp; UNIFORM_SCALE) != 0) {</span>
+<span class="add">                m = (m &gt;= 1) ? 2.0 : 0.5;</span>
+        }
+<span class="add">            else {</span>
+<span class="add">                m *= ENHANCEMENT_FACTOR;</span>
+<span class="add">            }</span>
+<span class="add">        }</span>
+        transform(actionType &amp; type, m, point);
+    }
+};</pre></td></tr>
+<tr><td><pre>    unexpectedException("reset", exception);
+    return;
+}
+<span class="del">if (yAxisUpward) zoom.setToScale(+1, -1);</span>
+<span class="del">else             zoom.setToIdentity();</span>
+final AffineTransform transform=setVisibleArea(preferredArea,
+                                               zoomableBounds);
+change.concatenate(zoom);</pre></td>
+<td><pre>    unexpectedException("reset", exception);
+    return;
+}
+<span class="add">if (yAxisUpward) {</span>
+<span class="add">    zoom.setToScale(+1, -1);</span>
+<span class="add">}</span>
+<span class="add">else {</span>
+<span class="add">    zoom.setToIdentity();</span>
+<span class="add">}</span>
+final AffineTransform transform=setVisibleArea(preferredArea,
+                                               zoomableBounds);
+change.concatenate(zoom);</pre></td></tr>
+<tr><td><pre>    visible=XAffineTransform.inverseTransform(zoom, zoomableBounds, null);
+} catch (NoninvertibleTransformException exception) {
+    unexpectedException("getVisibleArea", exception);
+<span class="del">    visible=new Rectangle2D.Double(zoomableBounds.getCenterX(), zoomableBounds.getCenterY(), 0, 0);</span>
+}
+visibleArea.setRect(visible);
+return visible;</pre></td>
+<td><pre>    visible=XAffineTransform.inverseTransform(zoom, zoomableBounds, null);
+} catch (NoninvertibleTransformException exception) {
+    unexpectedException("getVisibleArea", exception);
+<span class="add">    visible = new Rectangle2D.Double(zoomableBounds.getCenterX(),</span>
+<span class="add">                                     zoomableBounds.getCenterY(), 0, 0);</span>
+}
+visibleArea.setRect(visible);
+return visible;</pre></td></tr>
+<tr><td><pre> * @param  logicalBounds Logical coordinates of the region to be displayed.
+ * @throws IllegalArgumentException if &lt;code&gt;source&lt;/code&gt; is empty.
+ */
+<span class="del">public final void setVisibleArea(final Rectangle2D logicalBounds) throws IllegalArgumentException {</span>
+    log("setVisibleArea", logicalBounds);
+    transform(setVisibleArea(logicalBounds, getZoomableBounds()));
+}</pre></td>
+<td><pre> * @param  logicalBounds Logical coordinates of the region to be displayed.
+ * @throws IllegalArgumentException if &lt;code&gt;source&lt;/code&gt; is empty.
+ */
+<span class="add">public void setVisibleArea(final Rectangle2D logicalBounds)</span>
+<span class="add">       throws IllegalArgumentException {</span>
+    log("setVisibleArea", logicalBounds);
+    transform(setVisibleArea(logicalBounds, getZoomableBounds()));
+}</pre></td></tr>
+<tr><td><pre> * @return Change to apply to the affine transform {@link #zoom}.
+ * @throws IllegalArgumentException if &lt;code&gt;source&lt;/code&gt; is empty.
+ */
+<span class="del">private AffineTransform setVisibleArea(Rectangle2D source, Rectangle2D dest) throws IllegalArgumentException {</span>
+    /*
+     * Verifies the validity of the rectangle &lt;code&gt;source&lt;/code&gt;. An
+     * invalid rectangle will be rejected. However, we will be more</pre></td>
+<td><pre> * @return Change to apply to the affine transform {@link #zoom}.
+ * @throws IllegalArgumentException if &lt;code&gt;source&lt;/code&gt; is empty.
+ */
+<span class="add">private AffineTransform setVisibleArea(Rectangle2D source, Rectangle2D dest)</span>
+<span class="add">                                       throws IllegalArgumentException {</span>
+    /*
+     * Verifies the validity of the rectangle &lt;code&gt;source&lt;/code&gt;. An
+     * invalid rectangle will be rejected. However, we will be more</pre></td></tr>
+<tr><td><pre>if ((type &amp; UNIFORM_SCALE) == UNIFORM_SCALE) {
+    if (fillPanel)
+    {
+<span class="del">             if (sy*sourceWidth  &gt; destWidth ) sx=sy;</span>
+<span class="del">        else if (sx*sourceHeight &gt; destHeight) sy=sx;</span>
+    }
+    else
+    {
+<span class="del">             if (sy*sourceWidth  &lt; destWidth ) sx=sy;</span>
+<span class="del">        else if (sx*sourceHeight &lt; destHeight) sy=sx;</span>
+    }
+}
+final AffineTransform change=AffineTransform.getTranslateInstance(
+                 (type &amp; TRANSLATE_X)!=0 ? dest.getCenterX()    : 0,
+                 (type &amp; TRANSLATE_Y)!=0 ? dest.getCenterY()    : 0);</pre></td>
+<td><pre>if ((type &amp; UNIFORM_SCALE) == UNIFORM_SCALE) {
+    if (fillPanel)
+    {
+<span class="add">             if (sy * sourceWidth  &gt; destWidth ) {</span>
+<span class="add">                 sx = sy;</span>
+    }
+<span class="add">        else if (sx * sourceHeight &gt; destHeight) {</span>
+<span class="add">            sy = sx;</span>
+<span class="add">        }</span>
+<span class="add">    }</span>
+    else
+    {
+<span class="add">             if (sy * sourceWidth  &lt; destWidth ) {</span>
+<span class="add">                 sx=sy;</span>
+    }
+<span class="add">        else if (sx * sourceHeight &lt; destHeight) {</span>
+<span class="add">            sy=sx;</span>
+}
+<span class="add">    }</span>
+<span class="add">}</span>
+final AffineTransform change=AffineTransform.getTranslateInstance(
+                 (type &amp; TRANSLATE_X)!=0 ? dest.getCenterX()    : 0,
+                 (type &amp; TRANSLATE_Y)!=0 ? dest.getCenterY()    : 0);</pre></td></tr>
+<tr><td><pre>}
+
+/**
+<span class="del"> * LINDA UP TO HERE</span>
+<span class="del"> Change the {@linkplain #zoom} by applying and affine transform. The</span>
+<span class="del"> * &lt;code&gt;change&lt;/code&gt; transform must express a change if logical units,</span>
+<span class="del"> * for example a translation in meters. This method is conceptually similar</span>
+<span class="del"> * to the following code:</span>
+ *
+ * &lt;pre&gt;
+ * {@link #zoom}.{@link AffineTransform#concatenate(AffineTransform) concatenate}(change);</pre></td>
+<td><pre>}
+
+/**
+<span class="add"> * Changes the {@linkplain #zoom} by applying an affine transform. The</span>
+<span class="add"> * &lt;code&gt;change&lt;/code&gt; transform must express a change in logical units,</span>
+<span class="add"> * for example, a translation in metres. This method is conceptually</span>
+<span class="add"> * similar to the following code:</span>
+ *
+ * &lt;pre&gt;
+ * {@link #zoom}.{@link AffineTransform#concatenate(AffineTransform) concatenate}(change);</pre></td></tr>
+<tr><td><pre> *
+ * @param  change The zoom change, as an affine transform in logical
+ *         coordinates. If &lt;code&gt;change&lt;/code&gt; is the identity transform,
+<span class="del"> *         then this method do nothing and listeners are not notified.</span>
+ */
+public void transform(final AffineTransform change) {
+    if (!change.isIdentity()) {</pre></td>
+<td><pre> *
+ * @param  change The zoom change, as an affine transform in logical
+ *         coordinates. If &lt;code&gt;change&lt;/code&gt; is the identity transform,
+<span class="add"> *         then this method does nothing and listeners are not notified.</span>
+ */
+public void transform(final AffineTransform change) {
+    if (!change.isIdentity()) {</pre></td></tr>
+<tr><td><pre>}
+
+/**
+<span class="del"> * Effectue un zoom, une translation ou une rotation sur le contenu de</span>
+<span class="del"> * &lt;code&gt;ZoomPane&lt;/code&gt;. Le type d'opération à effectuer dépend de</span>
+<span class="del"> * l'argument &lt;code&gt;operation&lt;/code&gt;:</span>
+ *
+ * &lt;ul&gt;
+<span class="del"> *   &lt;li&gt;{@link #TRANSLATE_X} effectue une translation le long de l'axe des</span>
+<span class="del"> *       &lt;var&gt;x&lt;/var&gt;. L'argument &lt;code&gt;amount&lt;/code&gt; spécifie la</span>
+<span class="del"> *       transformation à effectuer en nombre de pixels. Une valeur négative</span>
+<span class="del"> *       déplace vers la gauche tandis qu'une valeur positive déplace vers</span>
+<span class="del"> *       la droite.&lt;/li&gt;</span>
+<span class="del"> *   &lt;li&gt;{@link #TRANSLATE_Y} effectue une translation le long de l'axe des</span>
+<span class="del"> *       &lt;var&gt;y&lt;/var&gt;. L'argument &lt;code&gt;amount&lt;/code&gt; spécifie la</span>
+<span class="del"> *       transformation à effectuer en nombre de pixels. Une valeur négative</span>
+<span class="del"> *       déplace vers le haut tandis qu'une valeur positive déplace vers le</span>
+<span class="del"> *       bas.&lt;/li&gt;</span>
+<span class="del"> *   &lt;li&gt;{@link #UNIFORM_SCALE} effectue un zoom. L'argument</span>
+<span class="del"> *       &lt;code&gt;zoom&lt;/code&gt; spécifie le zoom à effectuer. Une valeur</span>
+<span class="del"> *       supérieure à 1 effectuera un zoom avant, tandis qu'une valeur</span>
+<span class="del"> *       comprise entre 0 et 1 effectuera un zoom arrière.&lt;/li&gt;</span>
+<span class="del"> *   &lt;li&gt;{@link #ROTATE} effectue une rotation. L'argument &lt;code&gt;zoom&lt;/code&gt;</span>
+<span class="del"> *       spécifie l'angle de rotation en radians.&lt;/li&gt;</span>
+<span class="del"> *   &lt;li&gt;{@link #RESET} Redéfinit le zoom à une échelle, rotation et</span>
+<span class="del"> *       translation par défaut. Cette opération aura pour effet de faire</span>
+<span class="del"> *       apparaître la totalité ou quasi-totalité du contenu de</span>
+<span class="del"> *       &lt;code&gt;ZoomPane&lt;/code&gt;.&lt;/li&gt;</span>
+<span class="del"> *   &lt;li&gt;{@link #DEFAULT_ZOOM} Effectue un zoom par défaut, proche du zoom</span>
+<span class="del"> *       maximal, qui fait voir les détails du contenu de</span>
+<span class="del"> *       &lt;code&gt;ZoomPane&lt;/code&gt; mais sans les grossir exégarément.&lt;/li&gt;</span>
+ * &lt;/ul&gt;
+ *
+<span class="del"> * @param  operation Type d'opération à effectuer.</span>
+<span class="del"> * @param  amount Translation en pixels ({@link #TRANSLATE_X} et</span>
+<span class="del"> *         {@link #TRANSLATE_Y}), facteur d'échelle ({@link #SCALE_X} et</span>
+<span class="del"> *         {@link #SCALE_Y}) ou angle de rotation en radians</span>
+<span class="del"> *         ({@link #ROTATE}). Dans les autres cas, cet argument est ignoré</span>
+<span class="del"> *         et peut être {@link Double#NaN}.</span>
+<span class="del"> * @param  center Centre du zoom ({@link #SCALE_X} et {@link #SCALE_Y}) ou</span>
+<span class="del"> *         de la rotation ({@link #ROTATE}), en coordonnées pixels. La</span>
+<span class="del"> *         valeur &lt;code&gt;null&lt;/code&gt; désigne une valeur par défaut, le plus</span>
+<span class="del"> *         souvent le centre de la fenêtre.</span>
+<span class="del"> * @throws UnsupportedOperationException si l'argument</span>
+<span class="del"> *         &lt;code&gt;operation&lt;/code&gt; n'est pas reconnu.</span>
+ */
+private void transform(final int operation,
+                       final double amount,</pre></td>
+<td><pre>}
+
+/**
+<span class="add"> * Carries out a zoom, a translation or a rotation on the contents of</span>
+<span class="add"> * &lt;code&gt;ZoomPane&lt;/code&gt;. The type of operation to carry out depends on the</span>
+<span class="add"> * &lt;code&gt;operation&lt;/code&gt; argument:</span>
+ *
+ * &lt;ul&gt;
+<span class="add"> *   &lt;li&gt;{@link #TRANSLATE_X} carries out a translation along the</span>
+<span class="add"> *       &lt;var&gt;x&lt;/var&gt; axis. The &lt;code&gt;amount&lt;/code&gt; argument specifies the</span>
+<span class="add"> *       transformation to perform in number of pixels. A negative value</span>
+<span class="add"> *       moves to the left whilst a positive value moves to the right.&lt;/li&gt;</span>
+<span class="add"> *   &lt;li&gt;{@link #TRANSLATE_Y} carries out a translation along the</span>
+<span class="add"> *       &lt;var&gt;y&lt;/var&gt; axis. The &lt;code&gt;amount&lt;/code&gt; argument specifies the</span>
+<span class="add"> *       transformation to perform in number of pixels. A negative value</span>
+<span class="add"> *       moves upwards whilst a positive value moves downwards.&lt;/li&gt;</span>
+<span class="add"> *   &lt;li&gt;{@link #UNIFORM_SCALE} carries out a zoom. The &lt;code&gt;zoom&lt;/code&gt;</span>
+<span class="add"> *       argument specifies the type of zoom to perform. A value greater</span>
+<span class="add"> *       than 1 will perform a zoom in whilst a value between 0 and 1 will</span>
+<span class="add"> *       perform a zoom out.&lt;/li&gt;</span>
+<span class="add"> *   &lt;li&gt;{@link #ROTATE} carries out a rotation. The &lt;code&gt;zoom&lt;/code&gt;</span>
+<span class="add"> *       argument specifies the rotation angle in radians.&lt;/li&gt;</span>
+<span class="add"> *   &lt;li&gt;{@link #RESET} Redefines the zoom to a default scale, rotation</span>
+<span class="add"> *       and translation. This operation displays all, or almost all, the</span>
+<span class="add"> *       contents of &lt;code&gt;ZoomPane&lt;/code&gt;.&lt;/li&gt;</span>
+<span class="add"> *   &lt;li&gt;{@link #DEFAULT_ZOOM} Carries out a default zoom, close to the</span>
+<span class="add"> *       maximum zoom, which shows the details of the contents of</span>
+<span class="add"> *       &lt;code&gt;ZoomPane&lt;/code&gt; but without enlarging them too much.&lt;/li&gt;</span>
+ * &lt;/ul&gt;
+ *
+<span class="add"> * @param  operation Type of operation to perform.</span>
+<span class="add"> * @param  amount ({@link #TRANSLATE_X} and</span>
+<span class="add"> *         {@link #TRANSLATE_Y}) translation in pixels, ({@link #SCALE_X}</span>
+<span class="add"> *         and {@link #SCALE_Y}) scale factor or ({@link #ROTATE}) rotation</span>
+<span class="add"> *         angle in radians.</span>
+<span class="add"> *         In other cases, this argument is ignored and can be</span>
+<span class="add"> *         {@link Double#NaN}.</span>
+<span class="add"> * @param  center Zoom centre ({@link #SCALE_X} and {@link #SCALE_Y}) or</span>
+<span class="add"> *         rotation centre ({@link #ROTATE}), in pixel coordinates. The</span>
+<span class="add"> *         value &lt;code&gt;null&lt;/code&gt; indicates a default value, more often</span>
+<span class="add"> *         not the centre of the window.</span>
+<span class="add"> * @throws UnsupportedOperationException if the &lt;code&gt;operation&lt;/code&gt;</span>
+<span class="add"> *         &lt;code&gt;operation&lt;/code&gt; argument isn't recognized.</span>
+ */
+private void transform(final int operation,
+                       final double amount,</pre></td></tr>
+<tr><td><pre>                     ((operation &amp; TRANSLATE_Y)!=0) ? amount : 0);
+} else {
+    /*
+<span class="del">     * Obtient les coordonnées (en pixels)</span>
+<span class="del">     * du centre de rotation ou du zoom.</span>
+     */
+    final double centerX;
+    final double centerY;</pre></td>
+<td><pre>                     ((operation &amp; TRANSLATE_Y)!=0) ? amount : 0);
+} else {
+    /*
+<span class="add">     * Obtains the coordinates (in pixels) of the rotation or</span>
+<span class="add">     * zoom centre.</span>
+     */
+    final double centerX;
+    final double centerY;</pre></td></tr>
+<tr><td><pre>        return;
+    }
+    /*
+<span class="del">     * On accepte les largeurs et hauteurs de 0. Si toutefois le</span>
+<span class="del">     * rectangle n'est pas valide (largeur ou hauteur négatif),</span>
+<span class="del">     * alors on terminera cette méthode sans rien faire. Aucun</span>
+<span class="del">     * zoom n'aura été effectué.</span>
+     */
+}
+if ((operation &amp; (ROTATE))!=0) {</pre></td>
+<td><pre>        return;
+    }
+    /*
+<span class="add">     * Zero lengths and widths are accepted.  If, however, the</span>
+<span class="add">     * rectangle isn't valid (negative length or width) then the</span>
+<span class="add">     * method will end without doing anything. No zoom will be</span>
+<span class="add">     * performed.</span>
+     */
+}
+if ((operation &amp; (ROTATE))!=0) {</pre></td></tr>
+<tr><td><pre>}
+
+/**
+<span class="del"> * Ajoute un objet à la liste des objets intéressés</span>
+<span class="del"> * à être informés des changements de zoom.</span>
+ */
+public void addZoomChangeListener(final ZoomChangeListener listener) {
+    listenerList.add(ZoomChangeListener.class, listener);</pre></td>
+<td><pre>}
+
+/**
+<span class="add"> * Adds an object to the list of objects interested in being notified</span>
+<span class="add"> * about zoom changes.</span>
+ */
+public void addZoomChangeListener(final ZoomChangeListener listener) {
+    listenerList.add(ZoomChangeListener.class, listener);</pre></td></tr>
+<tr><td><pre>}
+
+/**
+<span class="del"> * Retire un objet de la liste des objets intéressés</span>
+<span class="del"> * à être informés des changements de zoom.</span>
+ */
+public void removeZoomChangeListener(final ZoomChangeListener listener) {
+    listenerList.remove(ZoomChangeListener.class, listener);</pre></td>
+<td><pre>}
+
+/**
+<span class="add"> * Removes an object from the list of objects interested in being notified</span>
+<span class="add"> * about zoom changes.</span>
+ */
+public void removeZoomChangeListener(final ZoomChangeListener listener) {
+    listenerList.remove(ZoomChangeListener.class, listener);</pre></td></tr>
+<tr><td><pre>}
+
+/**
+<span class="del"> * Ajoute un objet à la liste des objets intéressés</span>
+<span class="del"> * à être informés des événements de la souris.</span>
+ */
+public void addMouseListener(final MouseListener listener) {
+    super.removeMouseListener(mouseSelectionTracker);</pre></td>
+<td><pre>}
+
+/**
+<span class="add"> * Adds an object to the list of objects interested in being notified</span>
+<span class="add"> * about mouse events.</span>
+ */
+public void addMouseListener(final MouseListener listener) {
+    super.removeMouseListener(mouseSelectionTracker);</pre></td></tr>
+<tr><td><pre>}
+
+/**
+<span class="del"> * Signale qu'un changement du zoom vient d'être effectué. Chaque objets</span>
+<span class="del"> * enregistrés par la méthode {@link #addZoomChangeListener} sera prévenu</span>
+<span class="del"> * du changement aussitôt que possible.</span>
+ *
+<span class="del"> * @param change Transformation affine qui représente le changement dans le</span>
+<span class="del"> *               zoom. Soit &lt;code&gt;oldZoom&lt;/code&gt; et &lt;code&gt;newZoom&lt;/code&gt; les</span>
+<span class="del"> *               transformations affines de l'ancien et du nouveau zoom</span>
+<span class="del"> *               respectivement. Alors la relation</span>
+<span class="del"> *</span>
+ * &lt;code&gt;newZoom=oldZoom.{@link AffineTransform#concatenate concatenate}(change)&lt;/code&gt;
+<span class="del"> *</span>
+<span class="del"> *               doit être respectée (aux erreurs d'arrondissements près).</span>
+<span class="del"> *               &lt;strong&gt;Notez que cette méthode peut modifier</span>
+<span class="del"> *               &lt;code&gt;change&lt;/code&gt;&lt;/strong&gt; pour combiner en une seule</span>
+<span class="del"> *               transformation plusieurs appels consécutifs de</span>
+<span class="del"> *               &lt;code&gt;fireZoomChanged&lt;/code&gt;.</span>
+ */
+protected void fireZoomChanged(final AffineTransform change) {
+    visibleArea.setRect(getVisibleArea());</pre></td>
+<td><pre>}
+
+/**
+<span class="add"> * Signals that a zoom change has taken place. Every object registered by</span>
+<span class="add"> * the {@link #addZoomChangeListener} method will be notified of the change</span>
+<span class="add"> * as soon as possible.</span>
+ *
+<span class="add"> * @param change Affine transform which represents the change in the zoom.</span>
+<span class="add"> *               That is &lt;code&gt;oldZoom&lt;/code&gt; and &lt;code&gt;newZoom&lt;/code&gt; are</span>
+<span class="add"> *               the affine transforms of the old and new zoom respectively.</span>
+<span class="add"> *               Therefore, the relation</span>
+ * &lt;code&gt;newZoom=oldZoom.{@link AffineTransform#concatenate concatenate}(change)&lt;/code&gt;
+<span class="add"> *               must be respected (to within rounding errors).</span>
+<span class="add"> *               &lt;strong&gt;Note: This method can modify</span>
+<span class="add"> *               &lt;code&gt;change&lt;/code&gt;&lt;/strong&gt; to combine several</span>
+<span class="add"> *               consecutive calls of</span>
+<span class="add"> *               &lt;code&gt;fireZoomChanged&lt;/code&gt; in a single transformation.</span>
+ */
+protected void fireZoomChanged(final AffineTransform change) {
+    visibleArea.setRect(getVisibleArea());</pre></td></tr>
+<tr><td><pre>}
+
+/**
+<span class="del"> * Préviens les classes dérivées que le zoom a changé. Contrairement à la</span>
+<span class="del"> * méthode {@link #fireZoomChanged} protégée, cette méthode privée ne</span>
+<span class="del"> * modifie aucun champ interne et n'essaye pas d'appeller d'autres méthodes</span>
+<span class="del"> * de &lt;code&gt;ZoomPane&lt;/code&gt; comme {@link #getVisibleArea}. On évite ainsi</span>
+<span class="del"> * une boucle sans fin lorsque cette méthode est appelée par {@link #reset}.</span>
+ */
+private void fireZoomChanged0(final AffineTransform change) {
+    /*
+<span class="del">     * Note: il faut lancer l'événement même si la transformation</span>
+<span class="del">     *       est la matrice identité, car certaine classe utilise</span>
+<span class="del">     *       ce truc pour mettre à jour les barres de défilements.</span>
+     */
+    if (change==null) {
+        throw new NullPointerException();</pre></td>
+<td><pre>}
+
+/**
+<span class="add"> * Notifies derived classes that the zoom has changed. Unlike the</span>
+<span class="add"> * protected {@link #fireZoomChanged} method, this private method doesn't</span>
+<span class="add"> * modify any internal field and doesn't attempt to call other</span>
+<span class="add"> * &lt;code&gt;ZoomPane&gt; methods such as {@link #getVisibleArea}. An infinite</span>
+<span class="add"> * loop is thereby avoided as this method is called by {@link #reset}.</span>
+ */
+private void fireZoomChanged0(final AffineTransform change) {
+    /*
+<span class="add">     * Note: the event must be fired even if the transformation</span>
+<span class="add">     *       is the identity matrix, because certain classes use</span>
+<span class="add">     *       this to update scrollbars.</span>
+     */
+    if (change==null) {
+        throw new NullPointerException();</pre></td></tr>
+<tr><td><pre>}
+
+/**
+<span class="del"> * Méthode appelée automatiquement après que l'utilisateur ait sélectionnée</span>
+<span class="del"> * une région à l'aide de la souris. L'implémentation par défaut zoom la</span>
+<span class="del"> * région &lt;code&gt;area&lt;/code&gt; sélectionnée. Les classes dérivées peuvent</span>
+<span class="del"> * redéfinir cette méthode pour entreprendre une autre action.</span>
+ *
+<span class="del"> * @param area Région sélectionnée par l'utilisateur, en coordonnées</span>
+<span class="del"> *        logiques.</span>
+ */
+protected void mouseSelectionPerformed(final Shape area) {
+    final Rectangle2D rect=(area instanceof Rectangle2D) ? (Rectangle2D) area : area.getBounds2D();</pre></td>
+<td><pre>}
+
+/**
+<span class="add"> * Method called automatically after the user selects an area with the</span>
+<span class="add"> * mouse. The default implementation zooms to the selected</span>
+<span class="add"> * &lt;code&gt;area&lt;/code&gt;. Derived classes can redefine this method in order</span>
+<span class="add"> * to carry out another action.</span>
+ *
+<span class="add"> * @param area Area selected by the user, in logical coordinates.</span>
+ */
+protected void mouseSelectionPerformed(final Shape area) {
+    final Rectangle2D rect=(area instanceof Rectangle2D) ? (Rectangle2D) area : area.getBounds2D();</pre></td></tr>
+<tr><td><pre>}
+
+/**
+<span class="del"> * Retourne la forme géométrique à utiliser pour délimiter une région.</span>
+<span class="del"> * Cette forme est généralement un rectangle mais pourrait aussi être</span>
+<span class="del"> * une ellipse, une flèche ou d'autres formes encore. Les coordonnées</span>
+<span class="del"> * de la forme retournée ne seront pas prises en compte. En fait, ces</span>
+<span class="del"> * coordonnées seront régulièrement écrasées.  Seule compte la classe</span>
+<span class="del"> * de la forme retournée (par exemple {@link java.awt.geom.Ellipse2D}</span>
+<span class="del"> * vs {@link java.awt.geom.Rectangle2D}) et ses paramètres non-reliés</span>
+<span class="del"> * à sa position (par exemple l'arrondissement des coins d'un rectangle</span>
+ * {@link java.awt.geom.RoundRectangle2D}).
+ *
+<span class="del"> * La forme retournée sera généralement d'une classe dérivée de</span>
+<span class="del"> * {@link RectangularShape}, mais peut aussi être de la classe</span>
+<span class="del"> * {@link Line2D}. &lt;strong&gt;Tout autre classe risque de lancer une</span>
+<span class="del"> * {@link ClassCastException} au moment de l'exécution&lt;/strong&gt;.</span>
+ *
+<span class="del"> * L'implémentation par défaut retourne toujours un objet</span>
+<span class="del"> * {@link java.awt.geom.Rectangle2D}.</span>
+ *
+<span class="del"> * @param  event Coordonnées logiques de la souris au moment ou le bouton a</span>
+<span class="del"> *         été enfoncé. Cette information peut être utilisée par les classes</span>
+<span class="del"> *         dérivées qui voudraient tenir compte de la position de la souris</span>
+<span class="del"> *         avant de choisir une forme géométrique.</span>
+<span class="del"> * @return Forme de la classe {link RectangularShape} ou {link Line2D}, ou</span>
+<span class="del"> *         &lt;code&gt;null&lt;/code&gt; pour indiquer qu'on ne veut pas faire de</span>
+<span class="del"> *         sélection avec la souris.</span>
+ */
+protected Shape getMouseSelectionShape(final Point2D point) {
+    return new Rectangle2D.Float();</pre></td>
+<td><pre>}
+
+/**
+<span class="add"> * Returns the geometric shape to be used to delimitate an area.</span>
+<span class="add"> * This shape is generally a rectangle but could also be an ellipse,</span>
+<span class="add"> * an arrow or another shape. The coordinates of the returned shape</span>
+<span class="add"> * won't be taken into account. In fact, these coordinates will often</span>
+<span class="add"> * be destroyed. The only things which count are the class of the</span>
+<span class="add"> * returned shape (e.g. {@link java.awt.geom.Ellipse2D} vs</span>
+<span class="add"> * {@link java.awt.geom.Rectangle2D}) and any of its parameters not</span>
+<span class="add"> * related to its position (e.g. corner rounding in a rectangle</span>
+ * {@link java.awt.geom.RoundRectangle2D}).
+ *
+<span class="add"> * The returned shape will generally be from a class derived from</span>
+<span class="add"> * {@link RectangularShape}, but can also be from the class</span>
+<span class="add"> * {@link Line2D}. &lt;strong&gt;Any other class risks firing a</span>
+<span class="add"> * {@link ClassCastException} at execution&lt;/strong&gt;.</span>
+ *
+<span class="add"> * The default implementation always returns a</span>
+<span class="add"> * {@link java.awt.geom.Rectangle2D} object.</span>
+ *
+<span class="add"> * @param  event Logical coordinates of the mouse at the moment the button</span>
+<span class="add"> *         is pressed. This information can be used by derived classes</span>
+<span class="add"> *         that wish to consider the mouse position before choosing a</span>
+<span class="add"> *         geometric shape.</span>
+<span class="add"> * @return Shape from the class {link RectangularShape} or {link Line2D},</span>
+<span class="add"> *         or &lt;code&gt;null&lt;/code&gt; to indicate that we do not want to select</span>
+<span class="add"> *         with the mouse.</span>
+ */
+protected Shape getMouseSelectionShape(final Point2D point) {
+    return new Rectangle2D.Float();</pre></td></tr>
+<tr><td><pre>}
+
+/**
+<span class="del"> * Indique si la loupe est visible. Par défaut, la loupe n'est pas visible.</span>
+<span class="del"> * Appelez {@link #setMagnifierVisible(boolean)} pour la faire apparaitre.</span>
+ */
+public boolean isMagnifierVisible() {
+    return magnifier!=null;</pre></td>
+<td><pre>}
+
+/**
+<span class="add"> * Indicates whether or not the magnifying glass is visible.  By default,</span>
+<span class="add"> * it is not visible. Call {@link #setMagnifierVisible(boolean)} to make it</span>
+<span class="add"> * appear.</span>
+ */
+public boolean isMagnifierVisible() {
+    return magnifier!=null;</pre></td></tr>
+<tr><td><pre>}
+
+/**
+<span class="del"> * Fait apparaître ou disparaître la loupe. Si la loupe n'était pas visible</span>
+<span class="del"> * et que cette méthode est appelée avec l'argument &lt;code&gt;true&lt;/code&gt;, alors</span>
+<span class="del"> * la loupe apparaîtra au centre de la fenêtre.</span>
+ */
+public void setMagnifierVisible(final boolean visible) {
+    setMagnifierVisible(visible, null);</pre></td>
+<td><pre>}
+
+/**
+<span class="add"> * Displays or hides the magnifying glass. If the magnifying glass is not</span>
+<span class="add"> * visible and this method is called with the argument &lt;code&gt;true&lt;/code&gt;,</span>
+<span class="add"> * the magnifying glass will appear at the centre of the window.</span>
+ */
+public void setMagnifierVisible(final boolean visible) {
+    setMagnifierVisible(visible, null);</pre></td></tr>
+<tr><td><pre>}
+
+/**
+<span class="del"> * Indique si l'affichage de la loupe est autorisée sur</span>
+<span class="del"> * cette composante. Par défaut, elle est autorisée.</span>
+ */
+public boolean isMagnifierEnabled() {
+    return magnifierEnabled;</pre></td>
+<td><pre>}
+
+/**
+<span class="add"> * Indicates whether or not the magnifying glass is allowed to be</span>
+<span class="add"> * displayed on this component.  By default, it is allowed.</span>
+ */
+public boolean isMagnifierEnabled() {
+    return magnifierEnabled;</pre></td></tr>
+<tr><td><pre>}
+
+/**
+<span class="del"> * Spécifie si l'affichage de la loupe est autorisée sur cette composante.</span>
+<span class="del"> * L'appel de cette méthode avec la valeur &lt;code&gt;false&lt;/code&gt; fera</span>
+<span class="del"> * disparaître la loupe, supprimera le choix "Afficher la loupe" du menu</span>
+<span class="del"> * contextuel et fera ignorer tous les appels à</span>
+<span class="del"> * &lt;code&gt;{@link #setMagnifierVisible setMagnifierVisible}(true)&lt;/code&gt;.</span>
+ */
+public void setMagnifierEnabled(final boolean enabled) {
+    magnifierEnabled=enabled;</pre></td>
+<td><pre>}
+
+/**
+<span class="add"> * Specifies whether or not the magnifying glass is allowed to be displayed</span>
+<span class="add"> * on this component. Calling this method with the value &lt;code&gt;false&lt;/code&gt;</span>
+<span class="add"> * will hide the magnifying glass, delete the choice "Display magnifying</span>
+<span class="add"> * glass" from the contextual menu and lead to all calls to</span>
+<span class="add"> * &lt;code&gt;{@link #setMagnifierVisible setMagnifierVisible}(true)&lt;/code&gt;</span>
+<span class="add"> * being ignored.</span>
+ */
+public void setMagnifierEnabled(final boolean enabled) {
+    magnifierEnabled=enabled;</pre></td></tr>
+<tr><td><pre>}
+
+/**
+<span class="del"> * Corrige les coordonnées d'un pixel pour tenir compte de la présence de la</span>
+<span class="del"> * loupe. La point &lt;code&gt;point&lt;/code&gt; doit contenir les coordonnées d'un</span>
+<span class="del"> * pixel à l'écran. Si la loupe est visible et que &lt;code&gt;point&lt;/code&gt; se</span>
+<span class="del"> * trouve sur cette loupe, alors ses coordonnées seront corrigées pour faire</span>
+<span class="del"> * comme si elle pointait sur le même pixel, mais en l'absence de la loupe.</span>
+<span class="del"> * En effet, la présence de la loupe peut déplacer la position apparante des</span>
+<span class="del"> * pixels.</span>
+ */
+public final void correctPointForMagnifier(final Point2D point) {
+    if (magnifier!=null &amp;&amp; magnifier.contains(point)) {</pre></td>
+<td><pre>}
+
+/**
+<span class="add"> * Corrects a pixel's coordinates to take into account the presence of the</span>
+<span class="add"> * magnifying glass. The point &lt;code&gt;point&lt;/code&gt; must contain the</span>
+<span class="add"> * coordinates of a pixel on the screen. If the magnifying glass is visible</span>
+<span class="add"> * and &lt;code&gt;point&lt;/code&gt; falls within it, point's coordinates will be</span>
+<span class="add"> * corrected to make as if it pointed at the pixel itself, but in the</span>
+<span class="add"> * absence of the magnifying glass. In effect, the presence of the</span>
+<span class="add"> * magnifying glass can move the apparent position of the pixels.</span>
+ */
+public final void correctPointForMagnifier(final Point2D point) {
+    if (magnifier!=null &amp;&amp; magnifier.contains(point)) {</pre></td></tr>
+<tr><td><pre>final double centerX = magnifier.getCenterX();
+final double centerY = magnifier.getCenterY();
+/*
+<span class="del"> * Le code suivant est équivalent au transformations ci-dessous.</span>
+<span class="del"> * Ces transformations doivent être identiques à celles qui sont</span>
+<span class="del"> * appliquées dans {@link #paintMagnifier}.</span>
+ *
+ *         translate(+centerX, +centerY);
+ *         scale    (magnifierPower, magnifierPower);</pre></td>
+<td><pre>final double centerX = magnifier.getCenterX();
+final double centerY = magnifier.getCenterY();
+/*
+<span class="add"> * The following code is equivalent to the following</span>
+<span class="add"> * transformations.</span>
+<span class="add"> * These transformations must be identical to those which</span>
+<span class="add"> * are applied in {@link #paintMagnifier}.</span>
+ *
+ *         translate(+centerX, +centerY);
+ *         scale    (magnifierPower, magnifierPower);</pre></td></tr>
+<tr><td><pre>}
+
+/**
+<span class="del"> * Fait apparaître ou disparaître la loupe. Si la loupe n'était pas visible</span>
+<span class="del"> * et que cette méthode est appelée avec l'argument &lt;code&gt;true&lt;/code&gt;, alors</span>
+<span class="del"> * la loupe apparaîtra centré sur la coordonnées spécifiée.</span>
+ *
+<span class="del"> * @param visible &lt;code&gt;true&lt;/code&gt; pour faire apparaître la loupe,</span>
+<span class="del"> *                ou &lt;code&gt;false&lt;/code&gt; pour la faire disparaître.</span>
+<span class="del"> * @param center  Coordonnée centrale à laquelle faire apparaître la loupe.</span>
+<span class="del"> *                Si la loupe était initialement invisible, elle apparaîtra</span>
+<span class="del"> *                centrée à cette coordonnée (ou au centre de l'écran si</span>
+<span class="del"> *                &lt;code&gt;center&lt;/code&gt; est nul). Si la loupe était déjà</span>
+<span class="del"> *                visible et que &lt;code&gt;center&lt;/code&gt; est non-nul, alors elle</span>
+<span class="del"> *                sera déplacée pour la centrer à la coordonnées spécifiée.</span>
+ */
+private void setMagnifierVisible(final boolean visible, final Point center) {
+    if (visible &amp;&amp; magnifierEnabled) {
+        if (magnifier==null) {
+<span class="del">            Rectangle bounds=getZoomableBounds(); // Do not modifiy the Rectangle!</span>
+            if (bounds.isEmpty()) bounds=new Rectangle(0,0,DEFAULT_SIZE,DEFAULT_SIZE);
+            final int size=Math.min(Math.min(bounds.width, bounds.height), DEFAULT_MAGNIFIER_SIZE);
+            final int centerX, centerY;</pre></td>
+<td><pre>}
+
+/**
+<span class="add"> * Displays or hides the magnifying glass. If the magnifying glass isn't</span>
+<span class="add"> * visible and this method is called with the argument &lt;code&gt;true&lt;/code&gt;,</span>
+<span class="add"> * the magnifying glass will be displayed centred on the specified</span>
+<span class="add"> * coordinate.</span>
+ *
+<span class="add"> * @param visible &lt;code&gt;true&lt;/code&gt; to display the magnifying glass or</span>
+<span class="add"> *                &lt;code&gt;false&lt;/code&gt; to hide it.</span>
+<span class="add"> * @param center  Central coordinate on which to display the magnifying</span>
+<span class="add"> *                glass.  If the magnifying glass was initially invisible,</span>
+<span class="add"> *                it will appear centred on this coordinate (or in the</span>
+<span class="add"> *                centre of the screen if &lt;code&gt;center&lt;/code&gt; is null). If</span>
+<span class="add"> *                the magnifying glass was already visible and</span>
+<span class="add"> *                &lt;code&gt;center&lt;/code&gt; is not null, it will be moved to</span>
+<span class="add"> *                centre it on the specified coordinate.</span>
+ */
+private void setMagnifierVisible(final boolean visible, final Point center) {
+    if (visible &amp;&amp; magnifierEnabled) {
+        if (magnifier==null) {
+<span class="add">            Rectangle bounds = getZoomableBounds(); // Do not modify the Rectangle!</span>
+            if (bounds.isEmpty()) bounds=new Rectangle(0,0,DEFAULT_SIZE,DEFAULT_SIZE);
+            final int size=Math.min(Math.min(bounds.width, bounds.height), DEFAULT_MAGNIFIER_SIZE);
+            final int centerX, centerY;</pre></td></tr>
+<tr><td><pre>}
+
+/**
+<span class="del"> * Ajoute au menu spécifié des options de navigations. Des menus</span>
+<span class="del"> * tels que "Zoom avant" et "Zoom arrière" seront automatiquement</span>
+<span class="del"> * ajoutés au menu avec les raccourcis-clavier appropriés.</span>
+ */
+public void buildNavigationMenu(final JMenu menu) {
+    buildNavigationMenu(menu, null);</pre></td>
+<td><pre>}
+
+/**
+<span class="add"> * Adds navigation options to the specified menu. Menus such as</span>
+<span class="add"> * "Zoom in" and "Zoom out" will be automatically added to the menu</span>
+<span class="add"> * together with the appropriate short-cut keys.</span>
+ */
+public void buildNavigationMenu(final JMenu menu) {
+    buildNavigationMenu(menu, null);</pre></td></tr>
+<tr><td><pre>}
+
+/**
+<span class="del"> * Ajoute au menu spécifié des options de navigations. Des menus</span>
+<span class="del"> * tels que "Zoom avant" et "Zoom arrière" seront automatiquement</span>
+<span class="del"> * ajoutés au menu avec les raccourcis-clavier appropriés.</span>
+ */
+private void buildNavigationMenu(final JMenu menu, final JPopupMenu popup) {
+    int groupIndex=0;</pre></td>
+<td><pre>}
+
+/**
+<span class="add"> * Adds navigation options to the specified menu. Menus such as</span>
+<span class="add"> * "Zoom in" and "Zoom out" will be automatically added to the menu</span>
+<span class="add"> * together with the appropriate short-cut keys.</span>
+ */
+private void buildNavigationMenu(final JMenu menu, final JPopupMenu popup) {
+    int groupIndex=0;</pre></td></tr>
+<tr><td><pre>final Action action=actionMap.get(ACTION_ID[i]);
+if (action!=null &amp;&amp; action.getValue(Action.NAME)!=null) {
+    /*
+<span class="del">     * Vérifie si le prochain item fait parti d'un nouveau groupe.</span>
+<span class="del">     * Si c'est la cas, il faudra ajouter un séparateur avant le</span>
+<span class="del">     * prochain menu.</span>
+     */
+    final int lastGroupIndex=groupIndex;
+    while ((ACTION_TYPE[i] &amp; GROUP[groupIndex]) == 0) {
+        groupIndex = (groupIndex+1) % GROUP.length;
+<span class="del">        if (groupIndex==lastGroupIndex) break;</span>
+    }
+    /*
+<span class="del">     * Ajoute un item au menu.</span>
+     */
+    if (menu!=null) {
+        if (groupIndex!=lastGroupIndex) menu.addSeparator();</pre></td>
+<td><pre>final Action action=actionMap.get(ACTION_ID[i]);
+if (action!=null &amp;&amp; action.getValue(Action.NAME)!=null) {
+    /*
+<span class="add">     * Checks whether the next item belongs to a new group.</span>
+<span class="add">     * If this is the case, it will be necessary to add a separator</span>
+<span class="add">     * before the next menu.</span>
+     */
+    final int lastGroupIndex=groupIndex;
+    while ((ACTION_TYPE[i] &amp; GROUP[groupIndex]) == 0) {
+        groupIndex = (groupIndex+1) % GROUP.length;
+<span class="add">        if (groupIndex == lastGroupIndex) {</span>
+<span class="add">            break;</span>
+    }
+<span class="add">    }</span>
+    /*
+<span class="add">     * Adds an item to the menu.</span>
+     */
+    if (menu!=null) {
+        if (groupIndex!=lastGroupIndex) menu.addSeparator();</pre></td></tr>
+<tr><td><pre>}
+
+/**
+<span class="del"> * Menu avec une position. Cette classe retient les coordonnées</span>
+<span class="del"> * exacte de l'endroit où a cliqué l'utilisateur lorsqu'il a</span>
+<span class="del"> * invoké ce menu.</span>
+ *
+ * @author Martin Desruisseaux
+ * @version 1.0</pre></td>
+<td><pre>}
+
+/**
+<span class="add"> * Menu with a position.  This class retains the exact coordinates of the</span>
+<span class="add"> * place the user clicked when this menu was invoked.</span>
+ *
+ * @author Martin Desruisseaux
+ * @version 1.0</pre></td></tr>
+<tr><td><pre> */
+private static final class PointPopupMenu extends JPopupMenu {
+    /**
+<span class="del">     * Coordonnées de l'endroit où</span>
+<span class="del">     * avait cliqué l'utilisateur.</span>
+     */
+    public final Point point;
+
+    /**
+<span class="del">     * Construit un menu en retenant</span>
+<span class="del">     * la coordonnée spécifiée.</span>
+     */
+    public PointPopupMenu(final Point point) {
+        this.point=point;</pre></td>
+<td><pre> */
+private static final class PointPopupMenu extends JPopupMenu {
+    /**
+<span class="add">     * Coordinates of the point the user clicked on.</span>
+     */
+    public final Point point;
+
+    /**
+<span class="add">     * Constructs a menu, retaining the specified coordinate.</span>
+     */
+    public PointPopupMenu(final Point point) {
+        this.point=point;</pre></td></tr>
+<tr><td><pre>}
+
+/**
+<span class="del"> * Méthode appelée automatiquement lorsque l'utilisateur a cliqué sur le</span>
+<span class="del"> * bouton droit de la souris. L'implémentation par défaut fait apparaître</span>
+<span class="del"> * un menu contextuel dans lequel figure des options de navigations.</span>
+ *
+<span class="del"> * @param  event Evénement de la souris contenant entre autre les</span>
+<span class="del"> *         coordonnées pointées.</span>
+<span class="del"> * @return Le menu contextuel, ou &lt;code&gt;null&lt;/code&gt; pour ne pas faire</span>
+<span class="del"> *         apparaître de menu.</span>
+ */
+protected JPopupMenu getPopupMenu(final MouseEvent event) {
+    if (getZoomableBounds().contains(event.getX(), event.getY())) {</pre></td>
+<td><pre>}
+
+/**
+<span class="add"> * Method called automatically when the user clicks on the right mouse</span>
+<span class="add"> * button.  The default implementation displays a contextual menu</span>
+<span class="add"> * containing navigation options.</span>
+ *
+<span class="add"> * @param  event Mouse event containing amongst others, the</span>
+<span class="add"> *         coordinates pointées???????????.</span>
+<span class="add"> * @return The contextual menu, or &lt;code&gt;null&lt;/code&gt; to avoid displaying</span>
+<span class="add"> *         the menu.</span>
+ */
+protected JPopupMenu getPopupMenu(final MouseEvent event) {
+    if (getZoomableBounds().contains(event.getX(), event.getY())) {</pre></td></tr>
+<tr><td><pre>}
+
+/**
+<span class="del"> * Méthode appelée automatiquement lorsque l'utilisateur a cliqué sur le</span>
+<span class="del"> * bouton droit de la souris à l'intérieur de la loupe. L'implémentation</span>
+<span class="del"> * par défaut fait apparaître un menu contextuel dans lequel figure des</span>
+<span class="del"> * options relatives à la loupe.</span>
+ *
+<span class="del"> * @param  event Evénement de la souris contenant entre autre les</span>
+<span class="del"> *         oordonnées pointées.</span>
+<span class="del"> * @return Le menu contextuel, ou &lt;code&gt;null&lt;/code&gt; pour ne pas faire</span>
+<span class="del"> *         apparaître de menu.</span>
+ */
+protected JPopupMenu getMagnifierMenu(final MouseEvent event) {
+    final Resources resources = Resources.getResources(getLocale());</pre></td>
+<td><pre>}
+
+/**
+<span class="add"> * Method called automatically when the user clicks on the right mouse</span>
+<span class="add"> * button inside the magnifying glass. The default implementation displays</span>
+<span class="add"> * a contextual menu which contains magnifying glass options.</span>
+ *
+<span class="add"> * @param  event Mouse event containing amongst others, the</span>
+<span class="add"> *         coordinates ???? pointées.</span>
+<span class="add"> * @return The contextual menu, or &lt;code&gt;null&lt;/code&gt; to avoid displaying</span>
+<span class="add"> *         the menu.</span>
+ */
+protected JPopupMenu getMagnifierMenu(final MouseEvent event) {
+    final Resources resources = Resources.getResources(getLocale());</pre></td></tr>
+<tr><td><pre>}
+
+/**
+<span class="del"> * Fait apparaître le menu contextuel de navigation, à la</span>
+<span class="del"> * condition que l'évènement de la souris soit bien celui</span>
+<span class="del"> * qui fait normalement apparaître ce menu.</span>
+ */
+private void mayShowPopupMenu(final MouseEvent event) {
+    if ( event.getID()       == MouseEvent.MOUSE_PRESSED &amp;&amp;</pre></td>
+<td><pre>}
+
+/**
+<span class="add"> * Displays the navigation contextual menu, provided the mouse event is</span>
+<span class="add"> * in fact the one which normally displays this menu.</span>
+ */
+private void mayShowPopupMenu(final MouseEvent event) {
+    if ( event.getID()       == MouseEvent.MOUSE_PRESSED &amp;&amp;</pre></td></tr>
+<tr><td><pre>    SwingUtilities.convertPointToScreen(point, source);
+    screen.width  -= (size.width  + insets.right);
+    screen.height -= (size.height + insets.bottom);
+<span class="del">    if (point.x &gt; screen.width)  point.x = screen.width;</span>
+<span class="del">    if (point.y &gt; screen.height) point.y = screen.height;</span>
+<span class="del">    if (point.x &lt; insets.left)   point.x = insets.left;</span>
+<span class="del">    if (point.y &lt; insets.top)    point.y = insets.top;</span>
+    SwingUtilities.convertPointFromScreen(point, source);
+    popup.show(source, point.x, point.y);
+}</pre></td>
+<td><pre>    SwingUtilities.convertPointToScreen(point, source);
+    screen.width  -= (size.width  + insets.right);
+    screen.height -= (size.height + insets.bottom);
+<span class="add">    if (point.x &gt; screen.width) {</span>
+<span class="add">        point.x = screen.width;</span>
+<span class="add">    }</span>
+<span class="add">    if (point.y &gt; screen.height) {</span>
+<span class="add">        point.y = screen.height;</span>
+<span class="add">    }</span>
+<span class="add">    if (point.x &lt; insets.left) {</span>
+<span class="add">        point.x = insets.left;</span>
+<span class="add">    }</span>
+<span class="add">    if (point.y &lt; insets.top) {</span>
+<span class="add">        point.y = insets.top;</span>
+<span class="add">    }</span>
+    SwingUtilities.convertPointFromScreen(point, source);
+    popup.show(source, point.x, point.y);
+}</pre></td></tr>
+<tr><td><pre>}
+
+/**
+<span class="del"> * Méthode appelée automatiquement lorsque l'utilisateur a fait</span>
+<span class="del"> * tourné la roulette de la souris. Cette méthode effectue un</span>
+<span class="del"> * zoom centré sur la position de la souris.</span>
+ */
+private final void mouseWheelMoved(final MouseWheelEvent event)
+{</pre></td>
+<td><pre>}
+
+/**
+<span class="add"> * Method called automatically when user moves the mouse wheel. This method</span>
+<span class="add"> * performs a zoom centred on the mouse position.</span>
+ */
+private final void mouseWheelMoved(final MouseWheelEvent event)
+{</pre></td></tr>
+<tr><td><pre>}
+
+/**
+<span class="del"> * Méthode appelée chaque fois que la dimension</span>
+<span class="del"> * ou la position de la composante a changée.</span>
+ */
+private final void processSizeEvent(final ComponentEvent event)
+{</pre></td>
+<td><pre>}
+
+/**
+<span class="add"> * Method called each time the size or the position of the component</span>
+<span class="add"> * changes.</span>
+ */
+private final void processSizeEvent(final ComponentEvent event)
+{</pre></td></tr>
+<tr><td><pre>    magnifier.setClip(getZoomableBounds());
+}
+/*
+<span class="del"> * On n'appelle par {@link #repaint} parce qu'il y a déjà une commande</span>
+<span class="del"> * {@link #repaint} dans la queue.  Ainsi, le retraçage sera deux fois</span>
+<span class="del"> * plus rapide sous le JDK 1.3. On n'appele pas {@link #transform} non</span>
+<span class="del"> * plus car le zoom n'a pas vraiment changé;  on a seulement découvert</span>
+<span class="del"> * une partie de la fenêtre qui était cachée. Mais il faut tout de même</span>
+<span class="del"> * ajuster les barres de défilements.</span>
+ */
+final Object[] listeners=listenerList.getListenerList();
+for (int i=listeners.length; (i-=2)&gt;=0;) {</pre></td>
+<td><pre>    magnifier.setClip(getZoomableBounds());
+}
+/*
+<span class="add"> * {@link #repaint} isn't called because there is already a</span>
+<span class="add"> * {@link #repaint} command in the queue.  Therefore, the redraw will</span>
+<span class="add"> * be twice as quick under JDK 1.3. {@link #transform} isn't called</span>
+<span class="add"> * either because the zoom hasn't really changed; we have simply</span>
+<span class="add"> * discovered a part of the window which was hidden before. However,</span>
+<span class="add"> * we still need to adjust the scrollbars.</span>
+ */
+final Object[] listeners=listenerList.getListenerList();
+for (int i=listeners.length; (i-=2)&gt;=0;) {</pre></td></tr>
+<tr><td><pre>}
+
+/**
+<span class="del"> * Retourne un objet qui affiche ce &lt;code&gt;ZoomPane&lt;/code&gt;</span>
+<span class="del"> * avec des barres de défilements.</span>
+ */
+public JComponent createScrollPane() {
+    return new ScrollPane();</pre></td>
+<td><pre>}
+
+/**
+<span class="add"> * Returns an object which displays this &lt;code&gt;ZoomPane&lt;/code&gt;</span>
+<span class="add"> * with the scrollbars.</span>
+ */
+public JComponent createScrollPane() {
+    return new ScrollPane();</pre></td></tr>
+<tr><td><pre>/**
+ * The scroll panel for {@link ZoomPane}. The standard {@link JScrollPane}
+ * class is not used because it is difficult to get {@link JViewport} to
+<span class="del"> * cooperate with transformation already handled by {@link ZoomPane#zoom}.</span>
+ *
+ * @version 1.0
+ * @author Martin Desruisseaux</pre></td>
+<td><pre>/**
+ * The scroll panel for {@link ZoomPane}. The standard {@link JScrollPane}
+ * class is not used because it is difficult to get {@link JViewport} to
+<span class="add"> * cooperate with transformations already handled by {@link ZoomPane#zoom}.</span>
+ *
+ * @version 1.0
+ * @author Martin Desruisseaux</pre></td></tr>
+<tr><td><pre> */
+private final class ScrollPane extends JComponent implements PropertyChangeListener {
+    /**
+<span class="del">     * The horizontal scrolling bar, or &lt;code&gt;null&lt;/code&gt; if none.</span>
+     */
+    private final JScrollBar scrollbarX;
+
+    /**
+<span class="del">     * The vertical scrolling bar, or &lt;code&gt;null&lt;/code&gt; if none.</span>
+     */
+    private final JScrollBar scrollbarY;
+
+    /**
+<span class="del">     * Construct a scroll pane for the enclosing {@link ZoomPane}.</span>
+     */
+    public ScrollPane() {
+        setOpaque(false);
+        setLayout(new GridBagLayout());
+        /*
+<span class="del">         * Setup the scroll bars.</span>
+         */
+        if ((type &amp; TRANSLATE_X)!=0) {
+            scrollbarX=new JScrollBar(JScrollBar.HORIZONTAL);</pre></td>
+<td><pre> */
+private final class ScrollPane extends JComponent implements PropertyChangeListener {
+    /**
+<span class="add">     * The horizontal scrollbar, or &lt;code&gt;null&lt;/code&gt; if none.</span>
+     */
+    private final JScrollBar scrollbarX;
+
+    /**
+<span class="add">     * The vertical scrollbar, or &lt;code&gt;null&lt;/code&gt; if none.</span>
+     */
+    private final JScrollBar scrollbarY;
+
+    /**
+<span class="add">     * Constructs a scroll pane for the enclosing {@link ZoomPane}.</span>
+     */
+    public ScrollPane() {
+        setOpaque(false);
+        setLayout(new GridBagLayout());
+        /*
+<span class="add">         * Sets up the scrollbars.</span>
+         */
+        if ((type &amp; TRANSLATE_X)!=0) {
+            scrollbarX=new JScrollBar(JScrollBar.HORIZONTAL);</pre></td></tr>
+<tr><td><pre>    scrollbarY  = null;
+}
+/*
+<span class="del"> * Add the scroll bars in the scroll pane.</span>
+ */
+final Rectangle bounds = getZoomableBounds(); // Cached Rectangle: do not modify.
+final GridBagConstraints c = new GridBagConstraints();</pre></td>
+<td><pre>    scrollbarY  = null;
+}
+/*
+<span class="add"> * Adds the scrollbars in the scroll pane.</span>
+ */
+final Rectangle bounds = getZoomableBounds(); // Cached Rectangle: do not modify.
+final GridBagConstraints c = new GridBagConstraints();</pre></td></tr>
+<tr><td><pre>}
+
+/**
+<span class="del"> * Convenience method fetching a scroll bar model.</span>
+ * Should be a static method, but compiler doesn't
+ * allow this.
+ */</pre></td>
+<td><pre>}
+
+/**
+<span class="add"> * Convenience method which fetches a scrollbar model.</span>
+ * Should be a static method, but compiler doesn't
+ * allow this.
+ */</pre></td></tr>
+<tr><td><pre>/**
+ * Invoked when this &lt;code&gt;ScrollPane&lt;/code&gt;  is added in
+<span class="del"> * a {@link Container}. This method register all required</span>
+ * listeners.
+ */
+public void addNotify() {</pre></td>
+<td><pre>/**
+ * Invoked when this &lt;code&gt;ScrollPane&lt;/code&gt;  is added in
+<span class="add"> * a {@link Container}. This method registers all required</span>
+ * listeners.
+ */
+public void addNotify() {</pre></td></tr>
+<tr><td><pre>/**
+ * Invoked when this &lt;code&gt;ScrollPane&lt;/code&gt; is removed from
+<span class="del"> * a {@link Container}. This method unregister all listeners.</span>
+ */
+public void removeNotify() {
+    ZoomPane.this.removePropertyChangeListener("zoom.insets", this);</pre></td>
+<td><pre>/**
+ * Invoked when this &lt;code&gt;ScrollPane&lt;/code&gt; is removed from
+<span class="add"> * a {@link Container}. This method unregisters all listeners.</span>
+ */
+public void removeNotify() {
+    ZoomPane.this.removePropertyChangeListener("zoom.insets", this);</pre></td></tr>
+<tr><td><pre>}
+
+/**
+<span class="del"> * Invoked when the zoomable area changed. This method will adjust</span>
+ * scroll bar's insets in order to keep scroll bars aligned in front
+ * of the zoomable area.
+ *
+<span class="del"> * Note: in current version, this is an undocumented capability.</span>
+<span class="del"> *       Class {@link RangeBar} use it, but it is experimental.</span>
+ *       It may change in a future version.
+ */
+public void propertyChange(final PropertyChangeEvent event) {</pre></td>
+<td><pre>}
+
+/**
+<span class="add"> * Invoked when the zoomable area changes. This method will adjust</span>
+ * scroll bar's insets in order to keep scroll bars aligned in front
+ * of the zoomable area.
+ *
+<span class="add"> * Note: in the current version, this is an undocumented capability.</span>
+<span class="add"> *       Class {@link RangeBar} uses it, but it is experimental.</span>
+ *       It may change in a future version.
+ */
+public void propertyChange(final PropertyChangeEvent event) {</pre></td></tr>
+<tr><td><pre>}
+
+/**
+<span class="del"> * Synchronise la position et l'étendu des models &lt;var&gt;x&lt;/var&gt; et</span>
+<span class="del"> * &lt;var&gt;y&lt;/var&gt; avec la position du zoom. Les models &lt;var&gt;x&lt;/var&gt;</span>
+<span class="del"> * et &lt;var&gt;y&lt;/var&gt; sont généralement associés à des barres de defilements</span>
+<span class="del"> * horizontale et verticale. Lorsque la position d'une barre de défilement</span>
+<span class="del"> * est ajustée, le zomm sera ajusté en conséquence. Inversement, lorsque le</span>
+<span class="del"> * zoom est modifié, les positions et étendus des barres de défilements sont</span>
+<span class="del"> * ajustées en conséquence.</span>
+ *
+<span class="del"> * @param x Modèle de la barre de défilement horizontale,</span>
+<span class="del"> *          ou &lt;code&gt;null&lt;/code&gt; s'il n'y en a pas.</span>
+<span class="del"> * @param y Modèle de la barre de défilement verticale,</span>
+<span class="del"> *          ou &lt;code&gt;null&lt;/code&gt; s'il n'y en a pas.</span>
+ */
+public void tieModels(final BoundedRangeModel x, final BoundedRangeModel y) {
+    if (x!=null || y!=null) {</pre></td>
+<td><pre>}
+
+/**
+<span class="add"> * Synchronises the position and the range of the models &lt;var&gt;x&lt;/var&gt; and</span>
+<span class="add"> * &lt;var&gt;y&lt;/var&gt; with the position of the zoom.  The models &lt;var&gt;x&lt;/var&gt;</span>
+<span class="add"> * and &lt;var&gt;y&lt;/var&gt; are generally associated with horizontal and vertical</span>
+<span class="add"> * scrollbars.  When the position of a scrollbar is adjusted, the zoom is</span>
+<span class="add"> * consequently adjusted. Inversely, when the zoom is modified, the</span>
+<span class="add"> * positions and ranges of the scrollbars are consequently adjusted.</span>
+ *
+<span class="add"> * @param x Model of the horizontal scrollbar or &lt;code&gt;null&lt;/code&gt; if there</span>
+<span class="add"> *          isn't one.</span>
+<span class="add"> * @param y Model of the vertical scrollbar or &lt;code&gt;null&lt;/code&gt; if there</span>
+<span class="add"> *          isn't one.</span>
+ */
+public void tieModels(final BoundedRangeModel x, final BoundedRangeModel y) {
+    if (x!=null || y!=null) {</pre></td></tr>
+<tr><td><pre>}
+
+/**
+<span class="del"> * Annule la synchronisation entre les models &lt;var&gt;x&lt;/var&gt; et &lt;var&gt;y&lt;/var&gt;</span>
+<span class="del"> * spécifiés et le zoom de cet objet &lt;code&gt;ZoomPane&lt;/code&gt;. Les objets</span>
+<span class="del"> * {@link ChangeListener} et {@link ZoomChangeListener} qui avait été créés</span>
+<span class="del"> * seront supprimés.</span>
+ *
+<span class="del"> * @param x Modèle de la barre de défilement horizontale,</span>
+<span class="del"> *          ou &lt;code&gt;null&lt;/code&gt; s'il n'y en a pas.</span>
+<span class="del"> * @param y Modèle de la barre de défilement verticale,</span>
+<span class="del"> *          ou &lt;code&gt;null&lt;/code&gt; s'il n'y en a pas.</span>
+ */
+public void untieModels(final BoundedRangeModel x, final BoundedRangeModel y) {
+    final EventListener[] listeners=getListeners(ZoomChangeListener.class);</pre></td>
+<td><pre>}
+
+/**
+<span class="add"> * Cancels the synchronisation between the specified &lt;var&gt;x&lt;/var&gt; and</span>
+<span class="add"> * &lt;var&gt;y&lt;/var&gt; models and the zoom of this &lt;code&gt;ZoomPane&lt;/code&gt; object.</span>
+<span class="add"> * The {@link ChangeListener} and {@link ZoomChangeListener} objects that</span>
+<span class="add"> * were created are deleted.</span>
+ *
+<span class="add"> * @param x Model of the horizontal scrollbar or &lt;code&gt;null&lt;/code&gt; if there</span>
+<span class="add"> *          isn't one.</span>
+<span class="add"> * @param y Model of the vertical scrollbar or &lt;code&gt;null&lt;/code&gt; if there</span>
+<span class="add"> *          isn't one.</span>
+ */
+public void untieModels(final BoundedRangeModel x, final BoundedRangeModel y) {
+    final EventListener[] listeners=getListeners(ZoomChangeListener.class);</pre></td></tr>
+<tr><td><pre>}
+
+/**
+<span class="del"> * Objet ayant la charge de synchronizer un objet {@link JScrollPane}</span>
+<span class="del"> * avec des barres de défilements. Bien que ce ne soit généralement pas</span>
+<span class="del"> * utile, il serait possible de synchroniser plusieurs paires d'objets</span>
+<span class="del"> * {@link BoundedRangeModel} sur un  même objet &lt;code&gt;ZoomPane&lt;/code&gt;.</span>
+ *
+ * @author Martin Desruisseaux
+ * @version 1.0</pre></td>
+<td><pre>}
+
+/**
+<span class="add"> * Object responsible for synchronizing a {@link JScrollPane} object with</span>
+<span class="add"> * scrollbars.  Whilst not generally useful, it would be possible to</span>
+<span class="add"> * synchronize several pairs of {@link BoundedRangeModel} objects on one</span>
+<span class="add"> * &lt;code&gt;ZoomPane&lt;/code&gt; object.</span>
+ *
+ * @author Martin Desruisseaux
+ * @version 1.0</pre></td></tr>
+<tr><td><pre> */
+private final class Synchronizer implements ChangeListener, ZoomChangeListener {
+    /**
+<span class="del">     * Modèle à synchroniser avec {@link ZoomPane}.</span>
+     */
+    public final BoundedRangeModel xm,ym;
+
+    /**
+<span class="del">     * Indique si les barres de défilements sont en train</span>
+<span class="del">     * d'être ajustées en réponse à {@link #zoomChanged}.</span>
+<span class="del">     * Si c'est la cas, {@link #stateChanged} ne doit pas</span>
+<span class="del">     * faire d'autres ajustements.</span>
+     */
+    private transient boolean isAdjusting;
+
+    /**
+     * Cached &lt;code&gt;ZoomPane&lt;/code&gt; bounds. Used in order
+<span class="del">     * to avoid two many object allocation on the heap.</span>
+     */
+    private transient Rectangle bounds;
+
+    /**
+<span class="del">     * Construit un objet qui synchronisera une paire de</span>
+<span class="del">     * {@link BoundedRangeModel} avec {@link ZoomPane}.</span>
+     */
+    public Synchronizer(final BoundedRangeModel xm, final BoundedRangeModel ym) {
+        this.xm = xm;</pre></td>
+<td><pre> */
+private final class Synchronizer implements ChangeListener, ZoomChangeListener {
+    /**
+<span class="add">     * Model to synchronize with {@link ZoomPane}.</span>
+     */
+    public final BoundedRangeModel xm,ym;
+
+    /**
+<span class="add">     * Indicates whether the scrollbars are being adjusted in</span>
+<span class="add">     * response to {@link #zoomChanged}.</span>
+<span class="add">     * If this is the case, {@link #stateChanged} mustn't make any</span>
+<span class="add">     * other adjustments.</span>
+     */
+    private transient boolean isAdjusting;
+
+    /**
+     * Cached &lt;code&gt;ZoomPane&lt;/code&gt; bounds. Used in order
+<span class="add">     * to avoid too many object allocations on the heap.</span>
+     */
+    private transient Rectangle bounds;
+
+    /**
+<span class="add">     * Constructs an object which synchronises a pair of</span>
+<span class="add">     * {@link BoundedRangeModel} with {@link ZoomPane}.</span>
+     */
+    public Synchronizer(final BoundedRangeModel xm, final BoundedRangeModel ym) {
+        this.xm = xm;</pre></td></tr>
+<tr><td><pre>}
+
+/**
+<span class="del"> * Méthode appelée automatiquement chaque fois que la</span>
+<span class="del"> * position d'une des barres de défilement a changée.</span>
+ */
+public void stateChanged(final ChangeEvent event) {
+    if (!isAdjusting) {</pre></td>
+<td><pre>}
+
+/**
+<span class="add"> * Method called automatically each time the position of one of the</span>
+<span class="add"> * scrollbars changes.</span>
+ */
+public void stateChanged(final ChangeEvent event) {
+    if (!isAdjusting) {</pre></td></tr>
+<tr><td><pre>final boolean valueIsAdjusting=((BoundedRangeModel) event.getSource()).getValueIsAdjusting();
+if (paintingWhileAdjusting || !valueIsAdjusting) {
+    /*
+<span class="del">     * Scroll view coordinates are computed with the following</span>
+<span class="del">     * steps:</span>
+     *
+     *   1) Get the logical coordinates for the whole area.
+     *   2) Transform to pixel space using current zoom.
+<span class="del">     *   3) Clip to the scroll bars position (in pixels).</span>
+     *   4) Transform back to the logical space.
+     *   5) Set the visible area to the resulting rectangle.
+     */</pre></td>
+<td><pre>final boolean valueIsAdjusting=((BoundedRangeModel) event.getSource()).getValueIsAdjusting();
+if (paintingWhileAdjusting || !valueIsAdjusting) {
+    /*
+<span class="add">     * Scroll view coordinates are computed using the</span>
+<span class="add">     * following steps:</span>
+     *
+     *   1) Get the logical coordinates for the whole area.
+     *   2) Transform to pixel space using current zoom.
+<span class="add">     *   3) Clip to the scrollbar's position (in pixels).</span>
+     *   4) Transform back to the logical space.
+     *   5) Set the visible area to the resulting rectangle.
+     */</pre></td></tr>
+<tr><td><pre>}
+
+/**
+<span class="del"> * Méthode appelée chaque fois que le zoom a changé.</span>
+ *
+<span class="del"> * @param change Ignoré. Peut être nul, et sera</span>
+<span class="del"> *               effectivement parfois nul.</span>
+ */
+public void zoomChanged(final ZoomChangeEvent change) {
+    if (!isAdjusting) {</pre></td>
+<td><pre>}
+
+/**
+<span class="add"> * Method called each time the zoom changes.</span>
+ *
+<span class="add"> * @param change Ignored. Can be null and will effectively sometimes</span>
+<span class="add"> *               be null.</span>
+ */
+public void zoomChanged(final ZoomChangeEvent change) {
+    if (!isAdjusting) {</pre></td></tr>
+<tr><td><pre>}
+
+/**
+<span class="del"> * Procède à l'ajustement des valeurs d'un model. Les minimums et maximums</span>
+<span class="del"> * seront ajustés au besoin afin d'inclure la valeur et son étendu. Cet</span>
+<span class="del"> * ajustement est nécessaire pour éviter un comportement chaotique lorsque</span>
+<span class="del"> * l'utilisateur fait glisser l'ascensceur pendant qu'une partie du</span>
+<span class="del"> * graphique est en dehors de la zone qui était initialement prévue par</span>
+<span class="del"> * {@link #getArea}.</span>
+ */
+private static void setRangeProperties(final BoundedRangeModel model,
+                                       final double value, final int extent,</pre></td>
+<td><pre>}
+
+/**
+<span class="add"> * Adjusts the values of a model.  The minimums and maximums are adjusted</span>
+<span class="add"> * as needed in order to include the value and its range. This adjustment</span>
+<span class="add"> * is necessary in order to avoid chaotic behaviour when the user</span>
+<span class="add"> * drags the slider whilst a part of the graphic is outside the zone</span>
+<span class="add"> * initially planned for {@link #getArea}.</span>
+ */
+private static void setRangeProperties(final BoundedRangeModel model,
+                                       final double value, final int extent,</pre></td></tr>
+<tr><td><pre>}
+
+/**
+<span class="del"> * Modifie la position en pixels de la partie visible de</span>
+<span class="del"> * &lt;code&gt;ZoomPanel&lt;/code&gt;. Soit &lt;code&gt;viewSize&lt;/code&gt; les dimensions en</span>
+<span class="del"> * pixels qu'aurait &lt;code&gt;ZoomPane&lt;/code&gt; si sa surface visible couvrait</span>
+<span class="del"> * la totalité de la région {@link #getArea} avec le zoom courant (Note:</span>
+<span class="del"> * cette dimension &lt;code&gt;viewSize&lt;/code&gt; peut être obtenues par {@link</span>
+<span class="del"> * #getPreferredSize} si {@link #setPreferredSize} n'a pas été appelée avec</span>
+<span class="del"> * une valeur non-nulle). Alors par définition la région {@link #getArea}</span>
+<span class="del"> * convertit dans l'espace des pixels donnerait le rectangle</span>
+<span class="del"> *</span>
+ * &lt;code&gt;bounds=Rectangle(0,&amp;nbsp;0,&amp;nbsp;,viewSize.width,&amp;nbsp;,viewSize.height)&lt;/code&gt;.
+ *
+<span class="del"> * Cette méthode &lt;code&gt;scrollRectToVisible&lt;/code&gt; permet de définir la</span>
+<span class="del"> * sous-région de &lt;code&gt;bounds&lt;/code&gt; qui doit apparaître dans la fenêtre</span>
+<span class="del"> * &lt;code&gt;ZoomPane&lt;/code&gt;.</span>
+ */
+public void scrollRectToVisible(final Rectangle rect) {
+    Rectangle2D area=getArea();
+    if (isValid(area)) {
+        area=XAffineTransform.transform(zoom, area, null);
+<span class="del">        area.setRect(area.getX()+rect.getX(), area.getY()+rect.getY(), rect.getWidth(), rect.getHeight());</span>
+        try {
+            setVisibleArea(XAffineTransform.inverseTransform(zoom, area, area));
+        } catch (NoninvertibleTransformException exception) {</pre></td>
+<td><pre>}
+
+/**
+<span class="add"> * Modifies the position in pixels of the visible part of</span>
+<span class="add"> * &lt;code&gt;ZoomPane&lt;/code&gt;. &lt;code&gt;viewSize&lt;/code&gt; is the size</span>
+<span class="add"> * &lt;code&gt;ZoomPane&lt;/code&gt; would be (in pixels) if its visible surface</span>
+<span class="add"> * covered the whole of the {@link #getArea} region with the current</span>
+<span class="add"> * zoom (Note: &lt;code&gt;viewSize&lt;/code&gt; can be obtained by {@link</span>
+<span class="add"> * #getPreferredSize} if {@link #setPreferredSize} hasn't been called</span>
+<span class="add"> * with a non-null value). Therefore, by definition, the region</span>
+<span class="add"> * {@link #getArea} converted into pixel space would give the rectangle</span>
+ * &lt;code&gt;bounds=Rectangle(0,&amp;nbsp;0,&amp;nbsp;,viewSize.width,&amp;nbsp;,viewSize.height)&lt;/code&gt;.
+ *
+<span class="add"> * This &lt;code&gt;scrollRectToVisible&lt;/code&gt; method allows us to define the</span>
+<span class="add"> * sub-region of &lt;code&gt;bounds&lt;/code&gt; which must appear in the</span>
+<span class="add"> * &lt;code&gt;ZoomPane&lt;/code&gt; window.</span>
+ */
+public void scrollRectToVisible(final Rectangle rect) {
+    Rectangle2D area=getArea();
+    if (isValid(area)) {
+        area=XAffineTransform.transform(zoom, area, null);
+<span class="add">        area.setRect(area.getX() + rect.getX(), area.getY() + rect.getY(),</span>
+<span class="add">                     rect.getWidth(), rect.getHeight());</span>
+        try {
+            setVisibleArea(XAffineTransform.inverseTransform(zoom, area, area));
+        } catch (NoninvertibleTransformException exception) {</pre></td></tr>
+<tr><td><pre>}
+
+/**
+<span class="del"> * Indique si cet objet &lt;code&gt;ZoomPane&lt;/code&gt; doit être redessiné pendant</span>
+<span class="del"> * que l'utilisateur déplace le glissoir des barres de défilements. Les</span>
+<span class="del"> * barres de défilements (ou autres models) concernées sont celles qui ont</span>
+<span class="del"> * été synchronisées avec cet objet &lt;code&gt;ZoomPane&lt;/code&gt; à l'aide de la</span>
+<span class="del"> * méthode {@link #tieModels}. La valeur par défaut est &lt;code&gt;false&lt;/code&gt;,</span>
+<span class="del"> * ce qui signifie que &lt;code&gt;ZoomPane&lt;/code&gt; attendra que l'utilisateur ait</span>
+<span class="del"> * relaché le glissoir avant de se redessiner.</span>
+ */
+public boolean isPaintingWhileAdjusting() {
+    return paintingWhileAdjusting;</pre></td>
+<td><pre>}
+
+/**
+<span class="add"> * Indicates whether or not this &lt;code&gt;ZoomPane&lt;/code&gt; object should be</span>
+<span class="add"> * repainted when the user moves the scrollbar slider. The scrollbars (or</span>
+<span class="add"> * other models) involved are those which have been synchronised with</span>
+<span class="add"> * this &lt;code&gt;ZoomPane&lt;/code&gt; object through the {@link #tieModels} method.</span>
+<span class="add"> * The default value is &lt;code&gt;false&lt;/code&gt;, which means that</span>
+<span class="add"> * &lt;code&gt;ZoomPane&lt;/code&gt; will wait until the user releases the slider</span>
+<span class="add"> * before repainting.</span>
+ */
+public boolean isPaintingWhileAdjusting() {
+    return paintingWhileAdjusting;</pre></td></tr>
+<tr><td><pre>}
+
+/**
+<span class="del"> * Définit si cet objet &lt;code&gt;ZoomPane&lt;/code&gt; devra redessiner la carte</span>
+<span class="del"> * pendant que l'utilisateur déplace le glissoir des barres de défilements.</span>
+<span class="del"> * Il vaut mieux avoir un ordinateur assez rapide pour donner la valeur</span>
+<span class="del"> * &lt;code&gt;true&lt;/code&gt; à ce drapeau.</span>
+ */
+public void setPaintingWhileAdjusting(final boolean flag) {
+    paintingWhileAdjusting = flag;</pre></td>
+<td><pre>}
+
+/**
+<span class="add"> * Defines whether or not this &lt;code&gt;ZoomPane&lt;/code&gt; object should repaint</span>
+<span class="add"> * the map when the user moves the scrollbar slider.</span>
+<span class="add"> * A fast computer is recommended if this flag is to be set to</span>
+<span class="add"> * &lt;code&gt;true&lt;/code&gt;.</span>
+ */
+public void setPaintingWhileAdjusting(final boolean flag) {
+    paintingWhileAdjusting = flag;</pre></td></tr>
+<tr><td><pre>}
+
+/**
+<span class="del"> * Déclare qu'une partie de ce paneau a besoin d'être redéssinée. Cette</span>
+<span class="del"> * méthode ne fait que redéfinir la méthode de la classe parente pour tenir</span>
+<span class="del"> * compte du cas où la loupe serait affichée.</span>
+ */
+public void repaint(final long tm, final int x, final int y,
+                    final int width, final int height) {
+    super.repaint(tm, x, y, width, height);
+    if (magnifier!=null &amp;&amp; magnifier.intersects(x,y,width,height)) {
+<span class="del">        // Si la partie à dessiner est à l'intérieur de la loupe,</span>
+<span class="del">        // le fait que la loupe fasse un agrandissement nous oblige</span>
+<span class="del">        // à redessiner un peu plus que ce qui avait été demandé.</span>
+        repaintMagnifier();
+    }
+}
+
+/**
+<span class="del"> * Déclare que la loupe a besoin d'être redéssinée. Une commande</span>
+<span class="del"> * {@link #repaint()} sera envoyée avec comme coordonnées les limites</span>
+<span class="del"> * de la loupe (en tenant compte de sa bordure).</span>
+ */
+private void repaintMagnifier() {
+    final Rectangle bounds=magnifier.getBounds();</pre></td>
+<td><pre>}
+
+/**
+<span class="add"> * Declares that a part of this pane needs to be repainted. This method</span>
+<span class="add"> * simply redefines the method of the parent class in order to take into</span>
+<span class="add"> * account a case where the magnifying glass is displayed.</span>
+ */
+public void repaint(final long tm, final int x, final int y,
+                    final int width, final int height) {
+    super.repaint(tm, x, y, width, height);
+    if (magnifier!=null &amp;&amp; magnifier.intersects(x,y,width,height)) {
+<span class="add">        // If the part to paint is inside the magnifying glass,</span>
+<span class="add">        // the fact that the magnifying glass is zooming in means</span>
+<span class="add">        // we have to repaint a little more than that which was requested.</span>
+        repaintMagnifier();
+    }
+}
+
+/**
+<span class="add"> * Declares that the magnifying glass needs to be repainted. A</span>
+<span class="add"> * {@link #repaint()} command is sent with the bounds of the</span>
+<span class="add"> * magnifying glass as coordinates (taking into account its</span>
+<span class="add"> * outline).</span>
+ */
+private void repaintMagnifier() {
+    final Rectangle bounds=magnifier.getBounds();</pre></td></tr>
+<tr><td><pre>}
+
+/**
+<span class="del"> * Paints the magnifier. This method is invoked after</span>
+<span class="del"> * {@link #paintComponent(Graphics2D)} if a magnifier</span>
+ * is visible.
+ */
+protected void paintMagnifier(final Graphics2D graphics) {</pre></td>
+<td><pre>}
+
+/**
+<span class="add"> * Paints the magnifying glass. This method is invoked after</span>
+<span class="add"> * {@link #paintComponent(Graphics2D)} if a magnifying glass</span>
+ * is visible.
+ */
+protected void paintMagnifier(final Graphics2D graphics) {</pre></td></tr>
+<tr><td><pre>graphics.setColor (magnifierBorder);
+graphics.draw     (magnifier);
+graphics.setStroke(stroke);
+<span class="del">graphics.clip     (magnifier); // Coordonnées en pixels!</span>
+graphics.setColor (magnifierColor);
+graphics.fill     (magnifier.getBounds2D());
+graphics.setPaint (paint);</pre></td>
+<td><pre>graphics.setColor (magnifierBorder);
+graphics.draw     (magnifier);
+graphics.setStroke(stroke);
+<span class="add">graphics.clip     (magnifier); // Coordinates in pixels!</span>
+graphics.setColor (magnifierColor);
+graphics.fill     (magnifier.getBounds2D());
+graphics.setPaint (paint);</pre></td></tr>
+<tr><td><pre>    graphics.translate(+centerX, +centerY);
+    graphics.scale    (magnifierPower, magnifierPower);
+    graphics.translate(-centerX, -centerY);
+<span class="del">    // Note: les transformations effectuées ici doivent être identiques</span>
+<span class="del">    //       à celles qui sont faites dans {@link #pixelToLogical}.</span>
+    paintComponent    (graphics);
+}
+
+/**
+ * Paints this component. Subclass must override this method in order to
+<span class="del"> * drawn the &lt;code&gt;ZoomPane&lt;/code&gt; content. For must implementations, the</span>
+ * first line in this method will be
+ *
+ * &lt;code&gt;graphics.transform({@link #zoom})&lt;/code&gt;.</pre></td>
+<td><pre>    graphics.translate(+centerX, +centerY);
+    graphics.scale    (magnifierPower, magnifierPower);
+    graphics.translate(-centerX, -centerY);
+<span class="add">    // Note: the transformations performed here must be identical to those</span>
+<span class="add">    //       performed in {@link #pixelToLogical}.</span>
+    paintComponent    (graphics);
+}
+
+/**
+ * Paints this component. Subclass must override this method in order to
+<span class="add"> * draw the &lt;code&gt;ZoomPane&lt;/code&gt; content. For most implementations, the</span>
+ * first line in this method will be
+ *
+ * &lt;code&gt;graphics.transform({@link #zoom})&lt;/code&gt;.</pre></td></tr>
+<tr><td><pre>/**
+ * Paints this component. This method is declared &lt;code&gt;final&lt;/code&gt;
+<span class="del"> * in order to avoir unintentional overriding. Override</span>
+ * {@link #paintComponent(Graphics2D)} instead.
+ */
+protected final void paintComponent(final Graphics graphics) {</pre></td>
+<td><pre>/**
+ * Paints this component. This method is declared &lt;code&gt;final&lt;/code&gt;
+<span class="add"> * in order to avoid unintentional overriding. Override</span>
+ * {@link #paintComponent(Graphics2D)} instead.
+ */
+protected final void paintComponent(final Graphics graphics) {</pre></td></tr>
+<tr><td><pre>flag=IS_PAINTING;
+super.paintComponent(graphics);
+/*
+<span class="del"> * La méthode &lt;code&gt;JComponent.paintComponent(...)&lt;/code&gt; crée un objet &lt;code&gt;Graphics2D&lt;/code&gt;</span>
+<span class="del"> * temporaire, puis appelle &lt;code&gt;ComponentUI.update(...)&lt;/code&gt; avec en paramètre ce graphique.</span>
+<span class="del"> * Cette méthode efface le fond de l'écran, puis appelle &lt;code&gt;ComponentUI.paint(...)&lt;/code&gt;.</span>
+<span class="del"> * Or, cette dernière a été redéfinie plus haut (notre objet {@link #UI}) de sorte qu'elle</span>
+<span class="del"> * appelle elle-même {@link #paintComponent(Graphics2D)}. Un chemin compliqué, mais on a pas</span>
+<span class="del"> * tellement le choix et c'est somme toute assez efficace.</span>
+ */
+if (magnifier!=null) {
+    flag=IS_PAINTING_MAGNIFIER;</pre></td>
+<td><pre>flag=IS_PAINTING;
+super.paintComponent(graphics);
+/*
+<span class="add"> * The &lt;code&gt;JComponent.paintComponent(...)&lt;/code&gt; method creates a</span>
+<span class="add"> * temporary &lt;code&gt;Graphics2D&lt;/code&gt; object, then calls</span>
+<span class="add"> * &lt;code&gt;ComponentUI.update(...)&lt;/code&gt; with this graphic as a</span>
+<span class="add"> * parameter.  This method clears the screen background then calls</span>
+<span class="add"> * &lt;code&gt;ComponentUI.paint(...)&lt;/code&gt;.</span>
+<span class="add"> * This last method has been redefined further up (our {@link #UI})</span>
+<span class="add"> * object in such a way that it calls itself</span>
+<span class="add"> * {@link #paintComponent(Graphics2D)}. A complicated path, but we</span>
+<span class="add"> * don't have much choice and it is, after all, quite efficient.</span>
+ */
+if (magnifier!=null) {
+    flag=IS_PAINTING_MAGNIFIER;</pre></td></tr>
+<tr><td><pre>/**
+ * Prints this component. This method is declared &lt;code&gt;final&lt;/code&gt;
+<span class="del"> * in order to avoir unintentional overriding. Override</span>
+ * {@link #printComponent(Graphics2D)} instead.
+ */
+protected final void printComponent(final Graphics graphics) {</pre></td>
+<td><pre>/**
+ * Prints this component. This method is declared &lt;code&gt;final&lt;/code&gt;
+<span class="add"> * in order to avoid unintentional overriding. Override</span>
+ * {@link #printComponent(Graphics2D)} instead.
+ */
+protected final void printComponent(final Graphics graphics) {</pre></td></tr>
+<tr><td><pre>}
+
+/**
+<span class="del"> * Retourne la dimension (en pixels) qu'aurait &lt;code&gt;ZoomPane&lt;/code&gt; s'il</span>
+<span class="del"> * affichait la totalité de la région {@link #getArea} avec le zoom courant</span>
+<span class="del"> * ({@link #zoom}). Cette méthode est pratique pour déterminer les valeurs</span>
+<span class="del"> * maximales à affecter aux barres de défilement. Par exemple la barre</span>
+<span class="del"> * horizontale pourrait couvrir la plage &lt;code&gt;[0..viewSize.width]&lt;/code&gt;</span>
+<span class="del"> * tandis que la barre verticale pourrait couvrir la plage</span>
+ * &lt;code&gt;[0..viewSize.height]&lt;/code&gt;.
+ */
+private final Dimension getViewSize() {</pre></td>
+<td><pre>}
+
+/**
+<span class="add"> * Returns the size (in pixels) that &lt;code&gt;ZoomPane&lt;/code&gt; would have if</span>
+<span class="add"> * it displayed the whole of the {@link #getArea} region with the current</span>
+<span class="add"> * zoom ({@link #zoom}). This method is practical for determining the</span>
+<span class="add"> * maximum values to assign to the scrollbars. For example, the horizontal</span>
+<span class="add"> * bar could cover the range &lt;code&gt;[0..viewSize.width]&lt;/code&gt;</span>
+<span class="add"> * whilst the vertical bar could cover the range</span>
+ * &lt;code&gt;[0..viewSize.height]&lt;/code&gt;.
+ */
+private final Dimension getViewSize() {</pre></td></tr>
+<tr><td><pre>}
+
+/**
+<span class="del"> * Retourne les marges de cette composante. Cette méthode fonctionne comme</span>

[... 130 lines stripped ...]


Mime
View raw message