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