001package jmri.jmrit.display.layoutEditor;
002
003import java.awt.*;
004import java.awt.geom.Line2D;
005import java.awt.geom.Point2D;
006import java.awt.geom.Rectangle2D;
007
008import javax.annotation.Nonnull;
009import javax.swing.*;
010
011import jmri.util.*;
012
013/*
014* This is an intermediate component used to put the Layout Editor
015* into the component layers hierarchy so that objects can be drawn
016* in front of or behind layout editor objects.
017*
018* @author George Warner Copyright (c) 2017-2018
019 */
020class LayoutEditorComponent extends JComponent {
021
022    private final LayoutEditor layoutEditor;
023
024    // Antialiasing rendering
025    protected static final RenderingHints antialiasing = new RenderingHints(
026            RenderingHints.KEY_ANTIALIASING,
027            RenderingHints.VALUE_ANTIALIAS_ON);
028
029    protected LayoutEditorComponent(@Nonnull final LayoutEditor LayoutEditor) {
030        super();
031        this.layoutEditor = LayoutEditor;
032    }
033
034    /*
035    * {@inheritDoc}
036     */
037    @Override
038    public void paint(Graphics g) {
039        if (g instanceof Graphics2D) {
040            // layoutEditor.draw((Graphics2D) g);
041
042            Graphics2D g2 = (Graphics2D) g;
043
044            if (clipBounds != null) {
045                if (!clipBounds.isEmpty()) {
046                    if ((clipBounds.getWidth() > 0) && (clipBounds.getHeight() > 0)) {
047                        if (!clipBounds.equals(g2.getClipBounds())) {
048                            //log.debug("LEComponent.paint(); clipBounds: {}, oldClipBounds: {}",
049                            //        clipBounds, g2.getClipBounds());
050                            // intersect, never replace: clipBounds is the visible part of
051                            // the panel, used here to cull drawing, while the incoming clip
052                            // is the area Swing actually asked for. Replacing it would draw
053                            // track outside the dirty region, on top of higher level icons
054                            // that are not being repainted, as the target panel is a
055                            // JLayeredPane and so is not paint-optimized.
056                            g2.clip(clipBounds);
057                        }
058                    }
059                }
060            }
061            // Optional antialising, to eliminate (reduce) staircase on diagonal lines
062            if (layoutEditor.getAntialiasingOn()) {
063                g2.setRenderingHints(antialiasing);
064            }
065
066            // drawPositionableLabelBorder(g2);
067            // things that only get drawn in edit mode
068            if (layoutEditor.isEditable()) {
069                if (layoutEditor.getDrawGrid()) {
070                    drawPanelGrid(g2);
071                }
072                drawLayoutTracksHidden(g2);
073            }
074
075            drawShapes(g2, true);
076            drawTrackSegmentsDashed(g2);
077            drawLayoutTracksBallast(g2);
078            drawLayoutTracksTies(g2);
079            drawLayoutTracksRails(g2);
080            drawLayoutTracksBlockLines(g2);
081
082            drawPositionablePoints(g2, false);
083            drawPositionablePoints(g2, true);
084
085            drawShapes(g2, false);
086
087            drawDecorations(g2);
088
089            // things that only get drawn in edit mode
090            if (layoutEditor.isEditable()) {
091                drawLayoutTrackEditControls(g2);
092                drawShapeEditControls(g2);
093
094                drawMemoryRects(g2);
095                drawGlobalVariableRects(g2);
096                drawBlockContentsRects(g2);
097
098                if (layoutEditor.allControlling()) {
099                    drawTurnoutControls(g2);
100                }
101                drawSelectionRect(g2);
102                highLightSelection(g2);
103
104                drawTrackSegmentInProgress(g2);
105                drawShapeInProgress(g2);
106
107                if (layoutEditor.isDrawLayoutTracksLabel()) {
108                    drawLayoutTracksLabel(g2);
109                }
110            } else if (layoutEditor.getTurnoutCircles()) {
111                if (layoutEditor.allControlling()) {
112                    drawTurnoutControls(g2);
113                }
114            }
115        } else {
116            log.error("LayoutEditor drawing requires Graphics2D");
117        }
118    }
119
120    private void drawPanelGrid(Graphics2D g2) {
121        int wideMod = layoutEditor.gContext.getGridSize() * layoutEditor.gContext.getGridSize2nd();
122        int wideMin = layoutEditor.gContext.getGridSize() / 2;
123
124        // granulize puts these on getGridSize() increments
125        int minX = 0;
126        int minY = 0;
127        int maxX = (int) MathUtil.granulize(layoutEditor.gContext.getLayoutWidth(), layoutEditor.gContext.getGridSize());
128        int maxY = (int) MathUtil.granulize(layoutEditor.gContext.getLayoutHeight(), layoutEditor.gContext.getGridSize());
129
130        log.debug("drawPanelGrid: minX: {}, minY: {}, maxX: {}, maxY: {}", minX, minY, maxX, maxY);
131
132        Point2D startPt, stopPt;
133        BasicStroke narrow = new BasicStroke(1.0F, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
134        BasicStroke wide = new BasicStroke(2.0F, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
135
136        g2.setColor(Color.gray);
137        g2.setStroke(narrow);
138
139        // draw horizontal lines
140        for (int y = minY; y <= maxY; y += layoutEditor.gContext.getGridSize()) {
141            startPt = new Point2D.Double(minX, y);
142            stopPt = new Point2D.Double(maxX, y);
143
144            if ((y % wideMod) < wideMin) {
145                g2.setStroke(wide);
146                g2.draw(new Line2D.Double(startPt, stopPt));
147                g2.setStroke(narrow);
148            } else {
149                g2.draw(new Line2D.Double(startPt, stopPt));
150            }
151        }
152
153        // draw vertical lines
154        for (int x = minX; x <= maxX; x += layoutEditor.gContext.getGridSize()) {
155            startPt = new Point2D.Double(x, minY);
156            stopPt = new Point2D.Double(x, maxY);
157
158            if ((x % wideMod) < wideMin) {
159                g2.setStroke(wide);
160                g2.draw(new Line2D.Double(startPt, stopPt));
161                g2.setStroke(narrow);
162            } else {
163                g2.draw(new Line2D.Double(startPt, stopPt));
164            }
165        }
166    }
167
168    //
169    //  draw hidden layout tracks
170    //
171    private void drawLayoutTracksHidden(Graphics2D g2) {
172        LayoutTrackDrawingOptions ltdo = layoutEditor.getLayoutTrackDrawingOptions();
173        Stroke stroke = new BasicStroke(1.F);
174        Stroke dashedStroke = new BasicStroke(1.F,
175                BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 10.F,
176                new float[]{6.F, 4.F}, 0);
177
178        // setup for drawing hidden sideline rails
179        g2.setColor(ltdo.getSideRailColor());
180        g2.setStroke(stroke);
181        boolean main = false, block = false, hidden = true, dashed = false;
182        draw1(g2, main, block, hidden, dashed);
183        g2.setStroke(dashedStroke);
184        draw1(g2, main, block, hidden, dashed = true);
185
186        // setup for drawing mainline rails
187        main = true;
188        g2.setColor(ltdo.getMainRailColor());
189        g2.setStroke(stroke);
190        draw1(g2, main, block, hidden, dashed = false);
191        g2.setStroke(dashedStroke);
192        dashed = true;
193        draw1(g2, main, block, hidden, dashed);
194    }
195
196    //
197    //  draw dashed track segments
198    //
199    private void drawTrackSegmentsDashed(Graphics2D g2) {
200        LayoutTrackDrawingOptions ltdo = layoutEditor.getLayoutTrackDrawingOptions();
201        boolean main = false, block = false, hidden = false, dashed = true;
202
203        if (ltdo.getSideRailCount() > 0) {
204            // setup for drawing dashed sideline rails
205            int railWidth = ltdo.getSideRailWidth();
206            float[] dashArray = new float[]{6.F + railWidth, 4.F + railWidth};
207            g2.setStroke(new BasicStroke(
208                    railWidth,
209                    BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND,
210                    10.F, dashArray, 0));
211            g2.setColor(ltdo.getSideRailColor());
212            if ((ltdo.getSideRailCount() & 1) == 1) {
213                draw1(g2, main, block, hidden, dashed);
214            }
215            if (ltdo.getSideRailCount() >= 2) {
216                float railDisplacement = railWidth + (ltdo.getSideRailGap() / 2.F);
217                draw2(g2, main, railDisplacement, dashed);
218            }
219        }
220
221        if (ltdo.getMainRailCount() > 0) {
222            // setup for drawing dashed mainline rails
223            main = true;
224            int railWidth = ltdo.getMainRailWidth();
225            float[] dashArray = new float[]{6.F + railWidth, 4.F + railWidth};
226            g2.setStroke(new BasicStroke(
227                    railWidth,
228                    BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND,
229                    10.F, dashArray, 0));
230            g2.setColor(ltdo.getMainRailColor());
231            if ((ltdo.getMainRailCount() & 1) == 1) {
232                draw1(g2, main, block, hidden, dashed);
233            }
234            if (ltdo.getMainRailCount() >= 2) {
235                float railDisplacement = railWidth + (ltdo.getSideRailGap() / 2.F);
236                draw2(g2, main, railDisplacement, dashed);
237            }
238        }
239    }   // drawTrackSegmentsDashed
240
241    //
242    // draw layout track ballast
243    //
244    private void drawLayoutTracksBallast(Graphics2D g2) {
245        LayoutTrackDrawingOptions ltdo = layoutEditor.getLayoutTrackDrawingOptions();
246        boolean main = false, block = false, hidden = false, dashed = false;
247
248        // setup for drawing sideline ballast
249        int ballastWidth = ltdo.getSideBallastWidth();
250        if (ballastWidth > 0) {
251            g2.setStroke(new BasicStroke(ballastWidth,
252                    BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
253            g2.setColor(ltdo.getSideBallastColor());
254            draw1(g2, main, block, hidden, dashed);
255        }
256
257        // setup for drawing mainline ballast
258        ballastWidth = ltdo.getMainBallastWidth();
259        if (ballastWidth > 0) {
260            g2.setStroke(new BasicStroke(ballastWidth,
261                    BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
262            g2.setColor(ltdo.getMainBallastColor());
263            main = true;
264            draw1(g2, main, block, hidden, dashed);
265        }
266    }
267
268    //
269    // draw layout track ties
270    //
271    private void drawLayoutTracksTies(Graphics2D g2) {
272        LayoutTrackDrawingOptions ltdo = layoutEditor.getLayoutTrackDrawingOptions();
273
274        // setup for drawing sideline ties
275        int tieLength = ltdo.getSideTieLength();
276        int tieWidth = ltdo.getSideTieWidth();
277        int tieGap = ltdo.getSideTieGap();
278        if ((tieLength > 0) && (tieWidth > 0) && (tieGap > 0)) {
279            g2.setStroke(new BasicStroke(tieLength,
280                    BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 10.F,
281                    new float[]{tieWidth, tieGap}, 0));
282            g2.setColor(ltdo.getSideTieColor());
283            draw1(g2, false);  // main = false
284        }
285
286        // setup for drawing mainline ties
287        tieLength = ltdo.getMainTieLength();
288        tieWidth = ltdo.getMainTieWidth();
289        tieGap = ltdo.getMainTieGap();
290        if ((tieLength > 0) && (tieWidth > 0) && (tieGap > 0)) {
291            g2.setStroke(new BasicStroke(tieLength,
292                    BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 10.F,
293                    new float[]{tieWidth, tieGap}, 0));
294            g2.setColor(ltdo.getMainTieColor());
295            draw1(g2, true); // main = true
296        }
297    }
298
299    //
300    // draw layout track rails
301    //
302    private void drawLayoutTracksRails(Graphics2D g2) {
303        LayoutTrackDrawingOptions ltdo = layoutEditor.getLayoutTrackDrawingOptions();
304        int railWidth = ltdo.getSideRailWidth();
305        Color railColor = ltdo.getSideRailColor();
306
307        boolean main = false, block = false, hidden = false, dashed = false;
308
309        if (ltdo.getSideRailCount() > 1) {
310            // setup for drawing sideline rails
311            float railDisplacement = railWidth + (ltdo.getSideRailGap() / 2.F);
312            g2.setStroke(new BasicStroke(railWidth,
313                    BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND));
314            g2.setColor(railColor);
315            draw2(g2, main, railDisplacement);
316        }
317
318        if ((ltdo.getSideRailCount() & 1) == 1) {
319            // setup for drawing sideline rails
320            g2.setStroke(new BasicStroke(
321                    railWidth,
322                    BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
323            g2.setColor(railColor);
324            draw1(g2, main, block, hidden, dashed);
325        }
326
327        main = true;
328
329        railWidth = ltdo.getMainRailWidth();
330        railColor = ltdo.getMainRailColor();
331        if (ltdo.getMainRailCount() > 1) {
332            // setup for drawing mainline rails
333            float railDisplacement = railWidth + (ltdo.getMainRailGap() / 2.F);
334            g2.setStroke(new BasicStroke(railWidth,
335                    BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND));
336            g2.setColor(railColor);
337            draw2(g2, main, railDisplacement);
338        }
339        if ((ltdo.getMainRailCount() & 1) == 1) {
340            // setup for drawing mainline rails
341            g2.setStroke(new BasicStroke(
342                    railWidth,
343                    BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
344            g2.setColor(railColor);
345            dashed = false;
346            draw1(g2, main, block, hidden, dashed);
347        }
348    }   // drawLayoutTracksRails
349
350    //
351    // draw layout track block lines
352    //
353    private void drawLayoutTracksBlockLines(Graphics2D g2) {
354        LayoutTrackDrawingOptions ltdo = layoutEditor.getLayoutTrackDrawingOptions();
355
356        // setup for drawing sideline block lines
357        int blockLineWidth = ltdo.getSideBlockLineWidth();
358        float[] dashArray = new float[]{6.F + blockLineWidth, 4.F + blockLineWidth};
359
360        Stroke blockLineStroke;
361        int dashPercentageX10 = ltdo.getSideBlockLineDashPercentageX10();
362        if (dashPercentageX10 > 0) {
363            float[] blockLineDashArray = new float[]{
364                dashPercentageX10 + blockLineWidth,
365                10.F - dashPercentageX10 + blockLineWidth};
366            blockLineStroke = new BasicStroke(
367                    blockLineWidth,
368                    BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND,
369                    10.F, blockLineDashArray, 0);
370            g2.setStroke(blockLineStroke);
371        } else {
372            blockLineStroke = new BasicStroke(
373                    blockLineWidth,
374                    BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
375            g2.setStroke(new BasicStroke(
376                    blockLineWidth,
377                    BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND,
378                    10.F, dashArray, 0));
379        }
380
381        // note: color is set in layout track's draw1 when isBlock is true
382        boolean main = false, block = true, hidden = false, dashed = true;
383        draw1(g2, main, block, hidden, dashed);
384        g2.setStroke(blockLineStroke);
385        draw1(g2, main, block, hidden, dashed = false);
386
387        // setup for drawing mainline block lines
388        blockLineWidth = ltdo.getMainBlockLineWidth();
389        dashArray = new float[]{6.F + blockLineWidth, 4.F + blockLineWidth};
390
391        dashPercentageX10 = ltdo.getMainBlockLineDashPercentageX10();
392        if (dashPercentageX10 > 0) {
393            float[] blockLineDashArray = new float[]{
394                dashPercentageX10 + blockLineWidth,
395                10 - dashPercentageX10 + blockLineWidth};
396            blockLineStroke = new BasicStroke(
397                    blockLineWidth,
398                    BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND,
399                    10.F, blockLineDashArray, 0);
400            g2.setStroke(blockLineStroke);
401        } else {
402            blockLineStroke = new BasicStroke(
403                    blockLineWidth,
404                    BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
405            g2.setStroke(new BasicStroke(
406                    blockLineWidth,
407                    BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND,
408                    10.F, dashArray, 0));
409        }
410        // note: color is set in layout track's draw1 when isBlock is true
411        draw1(g2, main = true, block, hidden, dashed = true);
412        g2.setStroke(blockLineStroke);
413        dashed = false;
414        draw1(g2, main, block, hidden, dashed);
415    }
416
417    // isDashed defaults to false
418    private void draw1(Graphics2D g2,
419            boolean isMain,
420            boolean isBlock,
421            boolean isHidden) {
422        draw1(g2, isMain, isBlock, isHidden, false);
423    }
424
425    // isHidden defaults to false
426    private void draw1(Graphics2D g2,
427            boolean isMain,
428            boolean isBlock) {
429        draw1(g2, isMain, isBlock, false);
430    }
431
432    // isBlock defaults to false
433    private void draw1(Graphics2D g2, boolean isMain) {
434        draw1(g2, isMain, false);
435    }
436
437    // draw single line (ballast, ties & block lines)
438    private void draw1(Graphics2D g2,
439            boolean isMain,
440            boolean isBlock,
441            boolean isHidden,
442            boolean isDashed) {
443        for (LayoutTrackView layoutTrackView : layoutEditor.getLayoutTrackViews()) {
444            if (!(layoutTrackView instanceof PositionablePointView)) {
445                if (isHidden == layoutTrackView.isHidden()) {
446                    if ((layoutTrackView instanceof TrackSegmentView)) {
447                        if (((TrackSegmentView) layoutTrackView).isDashed() == isDashed) {
448                            layoutTrackView.draw1(g2, isMain, isBlock);
449                        }
450                    } else if (!isDashed) {
451                        layoutTrackView.draw1(g2, isMain, isBlock);
452                    }
453                }
454            }
455        }
456    }
457
458    // draw positionable points
459    private void drawPositionablePoints(Graphics2D g2, boolean isMain) {
460        for (PositionablePointView positionablePointView : layoutEditor.getPositionablePointViews()) {
461            positionablePointView.draw1(g2, isMain, false);
462        }
463    }
464
465    // isDashed defaults to false
466    private void draw2(Graphics2D g2, boolean isMain, float railDisplacement) {
467        draw2(g2, isMain, railDisplacement, false);
468    }
469
470    // draw parallel lines (rails)
471    private void draw2(Graphics2D g2, boolean isMain,
472            float railDisplacement, boolean isDashed) {
473        for (LayoutTrackView layoutTrackView : layoutEditor.getLayoutTrackViews()) {
474            if ((layoutTrackView instanceof TrackSegmentView)) {
475                if (((TrackSegmentView) layoutTrackView).isDashed() == isDashed) {
476                    layoutTrackView.draw2(g2, isMain, railDisplacement);
477                }
478            } else if (!isDashed) {
479                layoutTrackView.draw2(g2, isMain, railDisplacement);
480            }
481        }
482    }
483
484    // draw decorations
485    private void drawDecorations(Graphics2D g2) {
486        layoutEditor.getLayoutTrackViews().forEach((tr) -> tr.drawDecorations(g2));
487    }
488
489    // draw shapes
490    private void drawShapes(Graphics2D g2, boolean isBackground) {
491        layoutEditor.getLayoutShapes().forEach((s) -> {
492            if (isBackground == (s.getLevel() < 3)) {
493                s.draw(g2);
494            }
495        });
496    }
497
498    // draw track segment (in progress)
499    private void drawTrackSegmentInProgress(Graphics2D g2) {
500        // check for segment in progress
501        if (layoutEditor.isEditable() && (layoutEditor.beginTrack != null) && layoutEditor.getLayoutEditorToolBarPanel().trackButton.isSelected()) {
502            g2.setColor(layoutEditor.defaultTrackColor);
503            g2.setStroke(new BasicStroke(layoutEditor.gContext.getSidelineTrackWidth(), BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND));
504            g2.draw(new Line2D.Double(layoutEditor.beginLocation, layoutEditor.currentLocation));
505
506            // highlight unconnected endpoints of all tracks
507            Color highlightColor = ColorUtil.setAlpha(Color.red, 0.25);
508            Color connectColor = ColorUtil.setAlpha(Color.green, 0.5);
509            g2.setColor(highlightColor);
510            g2.setStroke(new BasicStroke(1.0F, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
511            for (LayoutTrack lt : layoutEditor.getLayoutTracks()) {
512                if (lt != layoutEditor.beginTrack) {
513                    LayoutTrackView ltv = layoutEditor.getLayoutTrackView(lt);
514                    if (lt == layoutEditor.foundTrack) {
515                        ltv.highlightUnconnected(g2);
516                        g2.setColor(connectColor);
517                        ltv.highlightUnconnected(g2, layoutEditor.foundHitPointType);
518                        g2.setColor(highlightColor);
519                    } else {
520                        ltv.highlightUnconnected(g2);
521                    }
522                }
523            }
524        }
525    }
526
527    // draw shape (in progress)
528    private void drawShapeInProgress(Graphics2D g2) {
529        // check for segment in progress
530        if (layoutEditor.getLayoutEditorToolBarPanel().shapeButton.isSelected()) {
531            // log.warn("drawShapeInProgress: selectedObject: " + selectedObject);
532            if ((layoutEditor.selectedObject != null)) {
533                g2.setColor(Color.DARK_GRAY);
534                g2.setStroke(new BasicStroke(3.0F, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND));
535                g2.draw(new Line2D.Double(layoutEditor.beginLocation, layoutEditor.currentLocation));
536            }
537        }
538    }
539
540    // draw layout track edit controls
541    private void drawLayoutTrackEditControls(Graphics2D g2) {
542        g2.setStroke(new BasicStroke(1.0F, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND));
543
544        layoutEditor.getLayoutTrackViews().forEach((tr) -> tr.drawEditControls(g2));
545    }
546
547    private void drawShapeEditControls(Graphics2D g2) {
548        g2.setStroke(new BasicStroke(1.0F, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND));
549        layoutEditor.getLayoutShapes().forEach((s) -> s.drawEditControls(g2));
550    }
551
552    // draw layout turnout controls
553    private void drawTurnoutControls(Graphics2D g2) {
554        g2.setStroke(new BasicStroke(1.0F, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND));
555        g2.setColor(layoutEditor.turnoutCircleColor);
556        g2.setBackground(layoutEditor.turnoutCircleThrownColor);
557
558        // loop over all turnouts
559        boolean editable = layoutEditor.isEditable();
560        layoutEditor.getLayoutTrackViews().forEach((tr) -> {
561            if (tr instanceof LayoutTurnoutView) {  //<== this includes LayoutSlips
562                LayoutTurnoutView lt = (LayoutTurnoutView) tr;
563                if (editable || !(lt.isHidden() || lt.isDisabled())) {
564                    lt.drawTurnoutControls(g2);
565                }
566            } else if (tr instanceof LayoutTurntableView) {
567                LayoutTurntableView lt = (LayoutTurntableView) tr;
568                if (editable || !lt.isHidden()) {
569                    lt.drawTurnoutControls(g2);
570                }
571            }else if (tr instanceof LayoutTraverserView) {
572                LayoutTraverserView lt = (LayoutTraverserView) tr;
573                if (editable || !lt.isHidden()) {
574                    lt.drawTurnoutControls(g2);
575                }
576            }
577        });
578
579    }
580
581    private void drawSelectionRect(Graphics2D g2) {
582        if (layoutEditor.selectionActive && (layoutEditor.selectionWidth != 0.0) && (layoutEditor.selectionHeight != 0.0)) {
583            // The Editor super class draws a dashed red selection rectangle...
584            // We're going to also draw a non-dashed yellow selection rectangle...
585            // This could be code stripped if the super-class implementation is "good enough"
586            Stroke stroke = g2.getStroke();
587            Color color = g2.getColor();
588
589            g2.setColor(new Color(204, 207, 88));
590            g2.setStroke(new BasicStroke(3.0F, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND));
591
592            g2.draw(layoutEditor.getSelectionRect());    // this sets _selectRect also
593
594            g2.setColor(color);
595            g2.setStroke(stroke);
596        } else {
597            layoutEditor.setSelectRect(null); // and clear it to turn it off
598        }
599    }
600
601    private void drawMemoryRects(Graphics2D g2) {
602        g2.setColor(layoutEditor.defaultTrackColor);
603        g2.setStroke(new BasicStroke(1.0F, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND));
604
605        layoutEditor.getMemoryLabelList().forEach((l) -> g2.draw(new Rectangle2D.Double(l.getX(), l.getY(), l.getSize().width, l.getSize().height)));
606    }
607
608    private void drawGlobalVariableRects(Graphics2D g2) {
609        g2.setColor(layoutEditor.defaultTrackColor);
610        g2.setStroke(new BasicStroke(1.0F, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND));
611
612        layoutEditor.getGlobalVariableLabelList().forEach((l) -> g2.draw(new Rectangle2D.Double(l.getX(), l.getY(), l.getSize().width, l.getSize().height)));
613    }
614
615    private void drawBlockContentsRects(Graphics2D g2) {
616        g2.setColor(layoutEditor.defaultTrackColor);
617        g2.setStroke(new BasicStroke(1.0F, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND));
618
619        layoutEditor.getBlockContentsLabelList().forEach((l) -> g2.draw(new Rectangle2D.Double(l.getX(), l.getY(), l.getSize().width, l.getSize().height)));
620    }
621
622    private void highLightSelection(Graphics2D g) {
623        Stroke stroke = g.getStroke();
624        Color color = g.getColor();
625        g.setColor(new Color(204, 207, 88));
626        g.setStroke(new BasicStroke(2.0f));
627
628        layoutEditor.getPositionalSelection().forEach((c) -> g.drawRect(c.getX(), c.getY(), c.maxWidth(), c.maxHeight()));
629
630        layoutEditor._layoutTrackSelection.stream().map((lt) -> {
631            LayoutTrackView ltv = layoutEditor.getLayoutTrackView(lt);
632            Rectangle2D r = ltv.getBounds();
633            if (r.isEmpty()) {
634                r = MathUtil.inset(r, -4.0);
635            }
636            //r = MathUtil.centerRectangleOnPoint(r, ltv.getCoordsCenter());
637            return r;
638        }).forEachOrdered(g::draw);
639
640        layoutEditor._layoutShapeSelection.stream().map((ls) -> {
641            Rectangle2D r = ls.getBounds();
642            if (r.isEmpty()) {
643                r = MathUtil.inset(r, -4.0);
644            }
645            //r = MathUtil.centerRectangleOnPoint(r, ls.getCoordsCenter());
646            return r;
647        }).forEachOrdered(g::draw);
648
649        g.setColor(color);
650        g.setStroke(stroke);
651    }
652
653    private void drawLayoutTracksLabel(Graphics2D g) {
654        g.setFont(new Font(Font.MONOSPACED, Font.BOLD, 12));
655        g.setColor(Color.red);
656        for (LayoutTrackView layoutTrackView : layoutEditor.getLayoutTrackViews()) {
657            layoutTrackView.drawLayoutTrackText(g);
658        }
659    }
660
661    /*
662    * {@inheritDoc}
663     */
664    @Override
665    public Rectangle getBounds() {
666        JComponent targetPanel = layoutEditor.getTargetPanel();
667        Rectangle2D targetBounds = targetPanel.getBounds();
668        Container parent = getParent();
669        if (parent != null) {   // if there is a parent...
670            Rectangle2D parentBounds = parent.getBounds();
671
672            // convert our origin to parent coordinates
673            Point2D origin = new Point2D.Double(
674                    targetBounds.getX() - parentBounds.getX(),
675                    targetBounds.getY() - parentBounds.getY());
676
677            return new Rectangle((int) origin.getX(), (int) origin.getY(),
678                    (int) targetBounds.getWidth(), (int) targetBounds.getHeight());
679        } else {
680            return MathUtil.rectangle2DToRectangle(targetBounds);
681        }
682    }
683
684    /*
685    * {@inheritDoc}
686     */
687    @Override
688    public Rectangle getBounds(Rectangle rv) {
689        rv.setBounds(getBounds());
690        return rv;
691    }
692
693    /*
694    * {@inheritDoc}
695     */
696    @Override
697    public int getX() {
698        Rectangle bounds = getBounds();
699        return (int) bounds.getX();
700    }
701
702    /*
703    * {@inheritDoc}
704     */
705    @Override
706    public int getY() {
707        Rectangle bounds = getBounds();
708        return (int) bounds.getY();
709    }
710
711    /*
712    * {@inheritDoc}
713     */
714    @Override
715    public int getWidth() {
716        Rectangle bounds = getBounds();
717        return (int) bounds.getWidth();
718    }
719
720    /*
721    * {@inheritDoc}
722     */
723    @Override
724    public int getHeight() {
725        Rectangle bounds = getBounds();
726        return (int) bounds.getHeight();
727    }
728
729    private Rectangle2D clipBounds = null;
730
731    public void setClip(Rectangle2D clipBounds) {
732        this.clipBounds = clipBounds;
733    }
734
735    // initialize logging
736    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LayoutEditorComponent.class);
737}