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