001    package org.springframework.richclient.widget.table;
002    
003    import org.apache.commons.beanutils.NestedNullException;
004    import org.apache.commons.beanutils.PropertyUtils;
005    import org.jdesktop.swingx.renderer.DefaultTableRenderer;
006    import org.jdesktop.swingx.renderer.FormatStringValue;
007    import org.springframework.richclient.util.RcpSupport;
008    
009    import javax.swing.*;
010    import javax.swing.border.Border;
011    import javax.swing.border.EmptyBorder;
012    import javax.swing.table.DefaultTableCellRenderer;
013    import javax.swing.table.JTableHeader;
014    import javax.swing.table.TableCellRenderer;
015    import java.awt.*;
016    import java.math.BigDecimal;
017    import java.text.*;
018    import java.util.Collection;
019    import java.util.Date;
020    import java.util.Locale;
021    
022    /**
023     * Voorziet een paar eenvoudige renderers voor gebruiksgemak.
024     */
025    public class TableCellRenderers
026    {
027    
028        public static final TableCellRenderer CENTER_ALIGNED_RENDERER = new AlignedRenderer(SwingConstants.CENTER);
029    
030        public static final TableCellRenderer RIGHT_ALIGNED_RENDERER = new AlignedRenderer(SwingConstants.RIGHT);
031    
032        public static final TableCellRenderer TOP_ALIGNED_RENDERER = new AlignedRenderer(SwingConstants.LEFT,
033                SwingConstants.TOP);
034        public static final TableCellRenderer BOTTOM_ALIGNED_RENDERER = new AlignedRenderer(SwingConstants.LEFT,
035                SwingConstants.BOTTOM);
036        public static final TableCellRenderer PERCENTAGE_RENDERER = new PercentageRenderer();
037        public static final TableCellRenderer MONEY_RENDERER = new BigDecimalRenderer(NumberFormat
038                .getCurrencyInstance(Locale.getDefault()));
039        public static final TableCellRenderer LEFT_ALIGNED_HEADER_RENDERER = new AlignedTableHeaderRenderer(
040                SwingConstants.LEFT);
041        public static final TableCellRenderer CENTER_ALIGNED_HEADER_RENDERER = new AlignedTableHeaderRenderer(
042                SwingConstants.CENTER);
043        public static final TableCellRenderer RIGHT_ALIGNED_HEADER_RENDERER = new AlignedTableHeaderRenderer(
044                SwingConstants.RIGHT);
045        public static final TableCellRenderer ENUM_RENDERER = new EnumTableCellRenderer();
046    
047        public static final TableCellRenderer FLAT_NUMBER_RENDERER = new FlatNumberRenderer();
048    
049        public static class FlatNumberRenderer extends DefaultTableRenderer
050        {
051    
052            private static NumberFormat format = NumberFormat.getIntegerInstance();
053            static
054            {
055                format.setGroupingUsed(false);
056            }
057    
058            public FlatNumberRenderer()
059            {
060                super(new FormatStringValue(format));
061            }
062        }
063    
064        public static class AlignedRenderer extends DefaultTableCellRenderer
065        {
066    
067            public AlignedRenderer(int horizontalAlignment)
068            {
069                super();
070                setHorizontalAlignment(horizontalAlignment);
071            }
072    
073            public AlignedRenderer(int horizontalAlignment, int verticalAlignment)
074            {
075                super();
076                setHorizontalAlignment(horizontalAlignment);
077                setVerticalAlignment(verticalAlignment);
078            }
079    
080            DateFormat formatter;
081    
082            @Override
083            public void setValue(Object value)
084            {
085                if (value != null && value instanceof Date)
086                {
087                    if (formatter == null)
088                    {
089                        formatter = DateFormat.getDateInstance();
090                    }
091                    setText(formatter.format(value));
092                }
093                else
094                {
095                    super.setValue(value);
096                }
097            }
098    
099        }
100    
101        public static class PercentageRenderer extends DefaultTableCellRenderer
102        {
103    
104            private static final DecimalFormatSymbols symbols = new DecimalFormatSymbols(Locale.getDefault());
105            private static final Format nonFractionalFormat = new DecimalFormat("###     %", symbols);
106            private static final Format fractionalFormat = new DecimalFormat("##0.00%", symbols);
107            private static final BigDecimal multiplyFactor = new BigDecimal("100");
108    
109            public PercentageRenderer()
110            {
111                setHorizontalAlignment(SwingConstants.RIGHT);
112            }
113    
114            @Override
115            protected void setValue(Object value)
116            {
117                if (value instanceof BigDecimal)
118                {
119                    BigDecimal percentage = ((BigDecimal) value).multiply(multiplyFactor);
120                    if (percentage.doubleValue() == percentage.intValue())
121                    {
122                        super.setValue(nonFractionalFormat.format(value));
123                    }
124                    else
125                    {
126                        super.setValue(fractionalFormat.format(value));
127                    }
128                }
129                else
130                {
131                    super.setValue(value);
132                }
133            }
134        }
135    
136        public static class BigDecimalRenderer extends DefaultTableCellRenderer
137        {
138    
139            private final BigDecimal multiplyFactor;
140            private final Format format;
141    
142            public BigDecimalRenderer(Format format)
143            {
144                this(null, format);
145            }
146    
147            public BigDecimalRenderer(BigDecimal multiplyFactor)
148            {
149                this(multiplyFactor, NumberFormat.getNumberInstance());
150            }
151    
152            public BigDecimalRenderer(BigDecimal multiplyFactor, Format format)
153            {
154                this(multiplyFactor, format, SwingConstants.RIGHT);
155            }
156    
157            public BigDecimalRenderer(BigDecimal multiplyFactor, Format format, int horizontalAlignment)
158            {
159                this.multiplyFactor = multiplyFactor;
160                this.format = format;
161                setHorizontalAlignment(horizontalAlignment);
162            }
163    
164            @Override
165            protected void setValue(Object value)
166            {
167                if (value instanceof BigDecimal)
168                {
169                    if (multiplyFactor != null)
170                    {
171                        value = ((BigDecimal) value).multiply(multiplyFactor);
172                    }
173                    super.setValue(format.format(value));
174                }
175                else
176                {
177                    super.setValue(value);
178                }
179            }
180        }
181    
182        public static class AlignedTableHeaderRenderer extends DefaultTableCellRenderer
183        {
184    
185            private int align = SwingConstants.CENTER;
186    
187            public AlignedTableHeaderRenderer(int align)
188            {
189                this.align = align;
190            }
191    
192            @Override
193            public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected,
194                    boolean hasFocus, int row, int column)
195            {
196                if (table != null)
197                {
198                    JTableHeader header = table.getTableHeader();
199                    if (header != null)
200                    {
201                        setForeground(header.getForeground());
202                        setBackground(header.getBackground());
203                        setFont(header.getFont());
204                    }
205                }
206    
207                setText((value == null) ? "" : value.toString() + " ");
208                setBorder(UIManager.getBorder("TableHeader.cellBorder"));
209                setHorizontalAlignment(align);
210                return this;
211            }
212        }
213    
214        public static class EnumTableCellRenderer extends DefaultTableCellRenderer
215        {
216    
217            public EnumTableCellRenderer()
218            {
219                super();
220            }
221    
222            @Override
223            public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected,
224                    boolean hasFocus, int row, int column)
225            {
226    
227                super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
228    
229                if (value == null)
230                {
231                    setValue("");
232                    setIcon(null);
233                }
234                else
235                {
236                    if (value instanceof Enum)
237                    {
238                        Enum valueEnum = (Enum) value;
239                        Class<? extends Enum> valueClass = valueEnum.getClass();
240                        setValue(RcpSupport.getMessage(valueClass.getName() + "." + valueEnum.name()));
241                        setIcon(RcpSupport.getIcon(valueClass.getName() + "." + valueEnum.name()));
242                    }
243                    else
244                    {
245                        setValue(value);
246                    }
247                }
248                return this;
249            }
250        }
251    
252        public static class ListPropertyCellRenderer extends JPanel implements TableCellRenderer
253        {
254    
255            protected String property;
256    
257            protected int verticalAlignment;
258    
259            protected int horizontalAlignment;
260    
261            protected Format format;
262    
263            protected float alignmentX;
264    
265            private static Border border = new EmptyBorder(1, 2, 1, 2);
266    
267            public ListPropertyCellRenderer(String property)
268            {
269                this(property, SwingConstants.LEFT, SwingConstants.CENTER);
270            }
271    
272            public ListPropertyCellRenderer(String property, int horizontalAlignment, int verticalAlignment)
273            {
274                this(property, horizontalAlignment, verticalAlignment, null);
275            }
276    
277            public ListPropertyCellRenderer(String property, int horizontalAlignment, int verticalAlignment,
278                    Format format)
279            {
280                this.property = property;
281                this.horizontalAlignment = horizontalAlignment;
282                this.verticalAlignment = verticalAlignment;
283                this.format = format;
284                switch (horizontalAlignment)
285                {
286                    case SwingConstants.LEFT :
287                        alignmentX = (float) 0.0;
288                        break;
289    
290                    case SwingConstants.CENTER :
291                        alignmentX = (float) 0.5;
292                        break;
293    
294                    case SwingConstants.RIGHT :
295                        alignmentX = (float) 1.0;
296                        break;
297    
298                    default :
299                        throw new IllegalArgumentException("Illegal horizontal alignment value");
300                }
301    
302                setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
303                setOpaque(true);
304                setBorder(border);
305            }
306    
307            public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected,
308                    boolean hasFocus, int row, int column)
309            {
310                removeAll();
311                invalidate();
312    
313                Color fg = table.getForeground();
314                Color bg = table.getBackground();
315    
316                if (isSelected)
317                {
318                    fg = table.getSelectionForeground();
319                    bg = table.getSelectionBackground();
320                }
321    
322                Font font = table.getFont();
323    
324                setFont(font);
325    
326                if (hasFocus)
327                {
328                    Border border = null;
329                    if (isSelected)
330                    {
331                        border = UIManager.getBorder("Table.focusSelectedCellHighlightBorder");
332                    }
333                    if (border == null)
334                    {
335                        border = UIManager.getBorder("Table.focusCellHighlightBorder");
336                    }
337                    setBorder(border);
338    
339                    if (!isSelected && table.isCellEditable(row, column))
340                    {
341                        Color col;
342                        col = UIManager.getColor("Table.focusCellForeground");
343                        if (col != null)
344                        {
345                            fg = col;
346                        }
347                        col = UIManager.getColor("Table.focusCellBackground");
348                        if (col != null)
349                        {
350                            bg = col;
351                        }
352                    }
353                }
354                else
355                {
356                    setBorder(border);
357                }
358    
359                super.setForeground(fg);
360                super.setBackground(bg);
361    
362                if (verticalAlignment != SwingConstants.TOP)
363                {
364                    add(Box.createVerticalGlue());
365                }
366    
367                Object[] values;
368                if (value instanceof Collection)
369                {
370                    values = ((Collection) value).toArray();
371                }
372                else
373                    throw new IllegalArgumentException("Value must be an instance of Collection.");
374    
375                for (int i = 0; i < values.length; i++)
376                {
377                    Object o = values[i];
378                    Object line;
379                    try
380                    {
381                        line = PropertyUtils.getProperty(o, property);
382                    }
383                    catch (NestedNullException e)
384                    {
385                        line = null;
386                    }
387                    catch (Exception e)
388                    {
389                        throw new RuntimeException("Error reading property " + property + " from object " + o, e);
390                    }
391                    JLabel lineLabel = new JLabel();
392                    lineLabel.setForeground(fg);
393                    lineLabel.setFont(font);
394                    setValue(lineLabel, line, i);
395                    add(lineLabel);
396                }
397    
398                int height_wanted = (int) getPreferredSize().getHeight();
399                if (height_wanted > table.getRowHeight(row))
400                    table.setRowHeight(row, height_wanted);
401    
402                if (verticalAlignment != SwingConstants.BOTTOM)
403                {
404                    add(Box.createVerticalGlue());
405                }
406                return this;
407            }
408    
409            protected void setValue(JLabel l, Object value, int lineNumber)
410            {
411                if (format != null && value != null)
412                    value = format.format(value);
413                l.setText(value == null ? " " : value.toString());
414                l.setHorizontalAlignment(horizontalAlignment);
415                l.setAlignmentX(alignmentX);
416                l.setOpaque(false);
417            }
418    
419        }
420    }
421