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        renderForeground(isSelected);
036        setToolTipText(((WayConnectionType) value).getToolTip());
037        renderBackground(getModel(table), null, isSelected);
038        return this;
039    }
040
041    @Override
042    public void paintComponent(Graphics g) {
043        super.paintComponent(g);
044        if (value == null || !value.isValid())
045            return;
046
047        int ymax = this.getSize().height - 1;
048        int xloop = 10;
049        int xowloop = 0;
050        if (value.isOnewayLoopForwardPart) {
051            xowloop = -3;
052        }
053        if (value.isOnewayLoopBackwardPart) {
054            xowloop = 3;
055        }
056
057        int xoff = this.getSize().width / 2;
058        if (value.isLoop) {
059            xoff -= xloop / 2 - 1;
060        }
061        int w = 2;
062        int p = 2 + w + 1;
063        int y1 = 0;
064        int y2 = 0;
065
066        if (value.linkPrev) {
067            g.setColor(Color.black);
068            if (value.isOnewayHead) {
069                g.fillRect(xoff - 1, 0, 3, 1);
070            } else {
071                g.fillRect(xoff - 1 + xowloop, 0, 3, 1);
072            }
073            y1 = 0;
074        } else {
075            if (value.isLoop) {
076                g.setColor(Color.black);
077                y1 = 5;
078                g.drawImage(corners, xoff, y1-3, xoff+3, y1, 0, 0, 3, 3, new Color(0, 0, 0, 0), null);
079                g.drawImage(corners, xoff+xloop-2, y1-3, xoff+xloop+1, y1, 2, 0, 5, 3, new Color(0, 0, 0, 0), null);
080                g.drawLine(xoff+3, y1-3, xoff+xloop-3, y1-3);
081            } else {
082                g.setColor(Color.red);
083                if (value.isOnewayHead) {
084                    g.drawRect(xoff-1, p - 3 - w, w, w);
085                } else {
086                    g.drawRect(xoff-1 + xowloop, p - 1 - w, w, w);
087                }
088                y1 = p;
089            }
090        }
091
092        if (value.linkNext) {
093            g.setColor(Color.black);
094            if (value.isOnewayTail) {
095                g.fillRect(xoff - 1, ymax, 3, 1);
096            } else {
097                g.fillRect(xoff - 1 + xowloop, ymax, 3, 1);
098            }
099            y2 = ymax;
100        } else {
101            if (value.isLoop) {
102                g.setColor(Color.black);
103                y2 = ymax - 5;
104                g.fillRect(xoff-1, y2+2, 3, 3);
105                g.drawLine(xoff, y2, xoff, y2+2);
106                g.drawImage(corners, xoff+xloop-2, y2+1, xoff+xloop+1, y2+4, 2, 2, 5, 5, new Color(0, 0, 0, 0), null);
107                g.drawLine(xoff+3-1, y2+3, xoff+xloop-3, y2+3);
108            } else {
109                g.setColor(Color.red);
110                if (value.isOnewayTail) {
111                    g.drawRect(xoff-1, ymax - p + 3, w, w);
112                } else {
113                    g.drawRect(xoff-1 + xowloop, ymax - p + 1, w, w);
114                }
115                y2 = ymax - p;
116            }
117        }
118
119        /* vertical lines */
120        g.setColor(Color.black);
121        if (value.isLoop) {
122            g.drawLine(xoff+xloop, y1, xoff+xloop, y2);
123        }
124
125        if (value.isOnewayHead) {
126            setDotted(g);
127            y1 = 7;
128
129            int[] xValues  = {xoff - xowloop + 1, xoff - xowloop + 1, xoff};
130            int[] yValues  = {ymax, y1+1, 1};
131            g.drawPolyline(xValues, yValues, 3);
132            unsetDotted(g);
133            g.drawLine(xoff + xowloop, y1+1, xoff, 1);
134        }
135
136        if (value.isOnewayTail) {
137            setDotted(g);
138            y2 = ymax - 7;
139
140            int[] xValues  = {xoff+1, xoff - xowloop + 1, xoff - xowloop + 1};
141            int[] yValues  = {ymax-1, y2, y1};
142            g.drawPolyline(xValues, yValues, 3);
143            unsetDotted(g);
144            g.drawLine(xoff + xowloop, y2, xoff, ymax-1);
145        }
146
147        if ((value.isOnewayLoopForwardPart || value.isOnewayLoopBackwardPart) && !value.isOnewayTail && !value.isOnewayHead) {
148            setDotted(g);
149            g.drawLine(xoff - xowloop+1, y1, xoff - xowloop+1, y2 + 1);
150            unsetDotted(g);
151        }
152
153        if (!value.isOnewayLoopForwardPart && !value.isOnewayLoopBackwardPart) {
154            g.drawLine(xoff, y1, xoff, y2);
155        }
156
157        g.drawLine(xoff+xowloop, y1, xoff+xowloop, y2);
158
159        /* special icons */
160        Image arrow = null;
161        switch (value.direction) {
162        case FORWARD:
163            arrow = arrowDown;
164            break;
165        case BACKWARD:
166            arrow = arrowUp;
167            break;
168        }
169        if (value.direction == Direction.ROUNDABOUT_LEFT) {
170            g.drawImage(roundabout_left, xoff-6, 1, null);
171        } else if (value.direction == Direction.ROUNDABOUT_RIGHT) {
172            g.drawImage(roundabout_right, xoff-6, 1, null);
173        }
174
175        if (!value.isOnewayLoopForwardPart && !value.isOnewayLoopBackwardPart &&
176                (arrow != null)) {
177            g.drawImage(arrow, xoff-3, (y1 + y2) / 2 - 2, null);
178        }
179
180        if (value.isOnewayLoopBackwardPart && value.isOnewayLoopForwardPart) {
181            if (arrow == arrowDown) {
182                arrow = arrowUp;
183            } else if (arrow == arrowUp) {
184                arrow = arrowDown;
185            }
186        }
187
188        if (arrow != null) {
189            g.drawImage(arrow, xoff+xowloop-3, (y1 + y2) / 2 - 2, null);
190        }
191    }
192
193    private static void setDotted(Graphics g) {
194        ((Graphics2D) g).setStroke(new BasicStroke(
195                1f,
196                BasicStroke.CAP_BUTT,
197                BasicStroke.CAP_BUTT,
198                5f,
199                new float[] {1f, 2f},
200                0f));
201    }
202
203    private static void unsetDotted(Graphics g) {
204        ((Graphics2D) g).setStroke(new BasicStroke());
205    }
206}