001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.gui.dialogs.relation;
003
004import java.awt.BasicStroke;
005import java.awt.Color;
006import java.awt.Component;
007import java.awt.Graphics;
008import java.awt.Graphics2D;
009import java.awt.Image;
010
011import javax.swing.JTable;
012
013import org.openstreetmap.josm.gui.dialogs.relation.sort.WayConnectionType;
014import org.openstreetmap.josm.gui.dialogs.relation.sort.WayConnectionType.Direction;
015import org.openstreetmap.josm.tools.ImageProvider;
016
017public class MemberTableLinkedCellRenderer extends MemberTableCellRenderer {
018
019    private static final Image arrowUp = ImageProvider.get("dialogs/relation", "arrowup").getImage();
020    private static final Image arrowDown = ImageProvider.get("dialogs/relation", "arrowdown").getImage();
021    private static final Image corners = ImageProvider.get("dialogs/relation", "roundedcorners").getImage();
022    private static final Image roundabout_right = ImageProvider.get("dialogs/relation", "roundabout_right_tiny").getImage();
023    private static final Image roundabout_left = ImageProvider.get("dialogs/relation", "roundabout_left_tiny").getImage();
024    private transient WayConnectionType value = new WayConnectionType();
025
026    @Override
027    public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus,
028            int row, int column) {
029
030        reset();
031        if (value == null)
032            return this;
033
034        this.value = (WayConnectionType) value;
035        setToolTipText(((WayConnectionType) value).getTooltip());
036        renderBackgroundForeground(getModel(table), null, isSelected);
037        return this;
038    }
039
040    @Override
041    public void paintComponent(Graphics g) {
042        super.paintComponent(g);
043        if (value == null || !value.isValid())
044            return;
045
046        int ymax = this.getSize().height - 1;
047        int xloop = 10;
048        int xowloop = 0;
049        if (value.isOnewayLoopForwardPart) {
050            xowloop = -3;
051        }
052        if (value.isOnewayLoopBackwardPart) {
053            xowloop = 3;
054        }
055
056        int xoff = this.getSize().width / 2;
057        if (value.isLoop) {
058            xoff -= xloop / 2 - 1;
059        }
060        int w = 2;
061        int p = 2 + w + 1;
062        int y1;
063        int y2;
064
065        if (value.linkPrev) {
066            g.setColor(Color.black);
067            if (value.isOnewayHead) {
068                g.fillRect(xoff - 1, 0, 3, 1);
069            } else {
070                g.fillRect(xoff - 1 + xowloop, 0, 3, 1);
071            }
072            y1 = 0;
073        } else {
074            if (value.isLoop) {
075                g.setColor(Color.black);
076                y1 = 5;
077                g.drawImage(corners, xoff, y1-3, xoff+3, y1, 0, 0, 3, 3, new Color(0, 0, 0, 0), null);
078                g.drawImage(corners, xoff+xloop-2, y1-3, xoff+xloop+1, y1, 2, 0, 5, 3, new Color(0, 0, 0, 0), null);
079                g.drawLine(xoff+3, y1-3, xoff+xloop-3, y1-3);
080            } else {
081                g.setColor(Color.red);
082                if (value.isOnewayHead) {
083                    g.drawRect(xoff-1, p - 3 - w, w, w);
084                } else {
085                    g.drawRect(xoff-1 + xowloop, p - 1 - w, w, w);
086                }
087                y1 = p;
088            }
089        }
090
091        if (value.linkNext) {
092            g.setColor(Color.black);
093            if (value.isOnewayTail) {
094                g.fillRect(xoff - 1, ymax, 3, 1);
095            } else {
096                g.fillRect(xoff - 1 + xowloop, ymax, 3, 1);
097            }
098            y2 = ymax;
099        } else {
100            if (value.isLoop) {
101                g.setColor(Color.black);
102                y2 = ymax - 5;
103                g.fillRect(xoff-1, y2+2, 3, 3);
104                g.drawLine(xoff, y2, xoff, y2+2);
105                g.drawImage(corners, xoff+xloop-2, y2+1, xoff+xloop+1, y2+4, 2, 2, 5, 5, new Color(0, 0, 0, 0), null);
106                g.drawLine(xoff+3-1, y2+3, xoff+xloop-3, y2+3);
107            } else {
108                g.setColor(Color.red);
109                if (value.isOnewayTail) {
110                    g.drawRect(xoff-1, ymax - p + 3, w, w);
111                } else {
112                    g.drawRect(xoff-1 + xowloop, ymax - p + 1, w, w);
113                }
114                y2 = ymax - p;
115            }
116        }
117
118        /* vertical lines */
119        g.setColor(Color.black);
120        if (value.isLoop) {
121            g.drawLine(xoff+xloop, y1, xoff+xloop, y2);
122        }
123
124        if (value.isOnewayHead) {
125            setDotted(g);
126            y1 = 7;
127
128            int[] xValues  = {xoff - xowloop + 1, xoff - xowloop + 1, xoff};
129            int[] yValues  = {ymax, y1+1, 1};
130            g.drawPolyline(xValues, yValues, 3);
131            unsetDotted(g);
132            g.drawLine(xoff + xowloop, y1+1, xoff, 1);
133        }
134
135        if (value.isOnewayTail) {
136            setDotted(g);
137            y2 = ymax - 7;
138
139            int[] xValues  = {xoff+1, xoff - xowloop + 1, xoff - xowloop + 1};
140            int[] yValues  = {ymax-1, y2, y1};
141            g.drawPolyline(xValues, yValues, 3);
142            unsetDotted(g);
143            g.drawLine(xoff + xowloop, y2, xoff, ymax-1);
144        }
145
146        if ((value.isOnewayLoopForwardPart || value.isOnewayLoopBackwardPart) && !value.isOnewayTail && !value.isOnewayHead) {
147            setDotted(g);
148            g.drawLine(xoff - xowloop+1, y1, xoff - xowloop+1, y2 + 1);
149            unsetDotted(g);
150        }
151
152        if (!value.isOnewayLoopForwardPart && !value.isOnewayLoopBackwardPart) {
153            g.drawLine(xoff, y1, xoff, y2);
154        }
155
156        g.drawLine(xoff+xowloop, y1, xoff+xowloop, y2);
157
158        /* special icons */
159        Image arrow;
160        switch (value.direction) {
161        case FORWARD:
162            arrow = arrowDown;
163            break;
164        case BACKWARD:
165            arrow = arrowUp;
166            break;
167        default:
168            arrow = null;
169        }
170        if (value.direction == Direction.ROUNDABOUT_LEFT) {
171            g.drawImage(roundabout_left, xoff-6, 1, null);
172        } else if (value.direction == Direction.ROUNDABOUT_RIGHT) {
173            g.drawImage(roundabout_right, xoff-6, 1, null);
174        }
175
176        if (!value.isOnewayLoopForwardPart && !value.isOnewayLoopBackwardPart &&
177                (arrow != null)) {
178            g.drawImage(arrow, xoff-3, (y1 + y2) / 2 - 2, null);
179        }
180
181        if (value.isOnewayLoopBackwardPart && value.isOnewayLoopForwardPart) {
182            if (arrow == arrowDown) {
183                arrow = arrowUp;
184            } else if (arrow == arrowUp) {
185                arrow = arrowDown;
186            }
187        }
188
189        if (arrow != null) {
190            g.drawImage(arrow, xoff+xowloop-3, (y1 + y2) / 2 - 2, null);
191        }
192    }
193
194    private static void setDotted(Graphics g) {
195        ((Graphics2D) g).setStroke(new BasicStroke(
196                1f,
197                BasicStroke.CAP_BUTT,
198                BasicStroke.CAP_BUTT,
199                5f,
200                new float[] {1f, 2f},
201                0f));
202    }
203
204    private static void unsetDotted(Graphics g) {
205        ((Graphics2D) g).setStroke(new BasicStroke());
206    }
207}