1/*
2 *******************************************************************************
3 * Copyright (C) 1997-2010, International Business Machines Corporation and    *
4 * others. All Rights Reserved.                                                *
5 *******************************************************************************
6 */
7package com.ibm.icu.dev.demo.holiday;
8
9import java.awt.Color;
10import java.awt.Dimension;
11import java.awt.Font;
12import java.awt.FontMetrics;
13import java.awt.Graphics;
14import java.awt.Insets;
15import java.awt.Panel;
16
17/**
18 * Various graphical borders. The border itself is a Panel so that it can
19 * contain other Components (i.e. it borders something). You use the
20 * HolidayBorderPanel like any other Panel: you set the layout that you prefer and
21 * add Components to it. Beware that a null layout does not obey the insets
22 * of the panel so if you use null layouts, adjust your measurements to
23 * handle the border by calling insets().
24 *
25 * @author  Andy Clark, Taligent Inc.
26 * @version 1.0
27 */
28public class HolidayBorderPanel extends Panel {
29    /**
30     * For serialization
31     */
32    private static final long serialVersionUID = 4669213306492461159L;
33    // Constants
34
35    /** Solid border. */
36    public final static int SOLID = 0;
37    /** A raised border. */
38    public final static int RAISED = 1;
39    /** A lowered border. */
40    public final static int LOWERED = 2;
41    /** An etched in border. */
42    public final static int IN = 3;
43    /** An etched out border. */
44    public final static int OUT = 4;
45
46    /** Left alignment. */
47    public final static int LEFT = 0;
48    /** Center alignment. */
49    public final static int CENTER = 1;
50    /** Right alignment. */
51    public final static int RIGHT = 2;
52
53    /** Default style (IN). */
54    public final static int DEFAULT_STYLE = IN;
55    /** Default thickness (10). */
56    public final static int DEFAULT_THICKNESS = 10;
57    /** Default thickness for solid borders (4). */
58    public final static int DEFAULT_SOLID_THICKNESS = 4;
59    /** Default thickness for raised borders (2). */
60    public final static int DEFAULT_RAISED_THICKNESS = 2;
61    /** Default thickness for lowered borders (2). */
62    public final static int DEFAULT_LOWERED_THICKNESS = 2;
63    /** Default thickness for etched-in borders (10). */
64    public final static int DEFAULT_IN_THICKNESS = 10;
65    /** Default thickness for etched-out borders (10). */
66    public final static int DEFAULT_OUT_THICKNESS = 10;
67    /** Default gap between border and contained component (5). */
68    public final static int DEFAULT_GAP = 5;
69    /** Default color (black). Applies to SOLID and etched borders. */
70    public final static Color DEFAULT_COLOR = Color.black;
71
72    /** Default font (TimesRoman,PLAIN,14). Only applies to etched borders. */
73    public final static Font DEFAULT_FONT = new Font("TimesRoman", Font.PLAIN, 14);
74    /** Default alignment (LEFT). Only applies to etched borders. */
75    public final static int DEFAULT_ALIGNMENT = LEFT;
76
77    // Data
78    private int style;
79    private int thickness;
80    private int gap;
81    private Color color;
82
83    private Font font;
84    private String text;
85    private int alignment;
86
87    /**
88     * Constructor. Makes default border.
89     */
90    public HolidayBorderPanel() {
91
92        // initialize data
93        style       = DEFAULT_STYLE;
94        thickness   = DEFAULT_THICKNESS;
95        gap         = DEFAULT_GAP;
96        color       = DEFAULT_COLOR;
97
98        text        = null;
99        font        = DEFAULT_FONT;
100        alignment   = DEFAULT_ALIGNMENT;
101
102        }
103
104    /**
105     * Constructor. Makes an etched IN border with given text caption.
106     *
107     * @param text  Text caption
108     */
109    public HolidayBorderPanel(String text) {
110        this();
111
112        style = IN;
113        this.text = text;
114        }
115
116    /**
117     * Constructor. Makes SOLID border with color and thickness given.
118     *
119     * @param color     The color for the border.
120     * @param thickness The thickness of the border.
121     */
122    public HolidayBorderPanel(Color color, int thickness) {
123        this();
124
125        style = SOLID;
126        this.color = color;
127        this.thickness = thickness;
128        }
129
130    /**
131     * Constructor. Makes a border of the given style with the default
132     * thickness for that style.
133     *
134     * @param style The style for this border.
135     */
136    public HolidayBorderPanel(int style) {
137        this();
138
139        // set thickness appropriate to this style
140        switch (style) {
141            case SOLID: thickness = DEFAULT_SOLID_THICKNESS; break;
142            case RAISED: thickness = DEFAULT_RAISED_THICKNESS; break;
143            case LOWERED: thickness = DEFAULT_LOWERED_THICKNESS; break;
144            case IN: thickness = DEFAULT_IN_THICKNESS; break;
145            case OUT: thickness = DEFAULT_OUT_THICKNESS; break;
146            default:
147                thickness = DEFAULT_THICKNESS;
148            }
149
150        this.style = style;
151        }
152
153    /**
154     * Constructor. Makes border with given style and thickness.
155     *
156     * @param style     The style for this border.
157     * @param thickness The thickness for this border.
158     */
159    public HolidayBorderPanel(int style, int thickness) {
160        this();
161
162        this.style = style;
163        this.thickness = thickness;
164        }
165
166    /**
167     * Returns the insets of this panel..
168     */
169    public Insets getInsets() {
170        int adjustment = 0;
171
172        // adjust for text string
173        if (style == IN || style == OUT) {
174            if (text != null && text.length() > 0) {
175                try {
176                    // set font and get info
177                    int height = getGraphics().getFontMetrics(font).getHeight();
178                    if (height > thickness)
179                        adjustment = height - thickness;
180                    }
181                catch (Exception e) {
182                    // nothing: just in case there is no graphics context
183                    //   at the beginning.
184                    System.out.print("");
185                    }
186                }
187            }
188
189        // return appropriate insets
190        int dist = thickness + gap;
191        return new Insets(dist + adjustment, dist, dist, dist);
192        }
193
194    /**
195     * Sets the style of the border
196     *
197     * @param style The new style.
198     */
199    public HolidayBorderPanel setStyle(int style) {
200
201        // set the style and re-layout the panel
202        this.style = style;
203        doLayout();
204        repaint();
205
206        return this;
207        }
208
209    /**
210     * Gets the style of the border
211     */
212    public int getStyle() {
213
214        return style;
215        }
216
217    /**
218     * Sets the thickness of the border.
219     *
220     * @param thickness The new thickness
221     */
222    public HolidayBorderPanel setThickness(int thickness) {
223
224        if (thickness > 0) {
225            this.thickness = thickness;
226            doLayout();
227            repaint();
228            }
229
230        return this;
231        }
232
233    /**
234     * Gets the thickness of the border.
235     */
236    public int getThickness() {
237
238        return thickness;
239        }
240
241    /**
242     * Sets the gap between the border and the contained Component.
243     *
244     * @param gap The new gap, in pixels.
245     */
246    public HolidayBorderPanel setGap(int gap) {
247
248        if (gap > -1) {
249            this.gap = gap;
250            doLayout();
251            repaint();
252            }
253
254        return this;
255        }
256
257    /**
258     * Gets the gap between the border and the contained Component.
259     */
260    public int getGap() {
261
262        return gap;
263        }
264
265    /**
266     * Sets the current color for SOLID borders and the caption text
267     * color for etched borders.
268     *
269     * @param color The new color.
270     */
271    public HolidayBorderPanel setColor(Color color) {
272
273        this.color = color;
274        if (style == SOLID || style == IN || style == OUT)
275            repaint();
276
277        return this;
278        }
279
280    /**
281     * Gets the current color for SOLID borders and the caption
282     * text color for etched borders.
283     */
284    public Color getColor() {
285
286        return color;
287        }
288
289    /**
290     * Sets the font. Only applies to etched borders.
291     */
292    public HolidayBorderPanel setTextFont(Font font) {
293
294        // set font
295        if (font != null) {
296            this.font = font;
297            if (style == IN || style == OUT) {
298                doLayout();
299                repaint();
300                }
301            }
302
303        return this;
304        }
305
306    /**
307     * Gets the font of the text. Only applies to etched borders.
308     */
309    public Font getTextFont() {
310
311        return font;
312        }
313
314    /**
315     * Sets the text. Only applies to etched borders.
316     *
317     * @param text  The new text.
318     */
319    public HolidayBorderPanel setText(String text) {
320
321        this.text = text;
322        if (style == IN || style == OUT) {
323            doLayout();
324            repaint();
325            }
326
327        return this;
328        }
329
330    /**
331     * Gets the text. Only applies to etched borders.
332     */
333    public String getText() {
334
335        return text;
336        }
337
338    /**
339     * Sets the text alignment. Only applies to etched borders.
340     *
341     * @param alignment The new alignment.
342     */
343    public HolidayBorderPanel setAlignment(int alignment) {
344
345        this.alignment = alignment;
346        if (style == IN || style == OUT) {
347            doLayout();
348            repaint();
349            }
350
351        return this;
352        }
353
354    /**
355     * Gets the text alignment.
356     */
357    public int getAlignment() {
358
359        return alignment;
360        }
361
362    /**
363     * Repaints the border.
364     *
365     * @param g The graphics context.
366     */
367    public void paint(Graphics g) {
368
369        // get current dimensions
370        Dimension size = getSize();
371        int width = size.width;
372        int height = size.height;
373
374        // set colors
375        Color light = getBackground().brighter().brighter().brighter();
376        Color dark = getBackground().darker().darker().darker();
377
378        // Draw border
379        switch (style) {
380            case RAISED:    // 3D Border (in or out)
381            case LOWERED:
382                Color topleft = null;
383                Color bottomright = null;
384
385                // set colors
386                if (style == RAISED) {
387                    topleft = light;
388                    bottomright = dark;
389                    }
390                else {
391                    topleft = dark;
392                    bottomright = light;
393                    }
394
395                // draw border
396                g.setColor(topleft);
397                for (int i = 0; i < thickness; i++) {
398                    g.drawLine(i, i, width - i - 2, i);
399                    g.drawLine(i, i + 1, i, height - i - 1);
400                    }
401                g.setColor(bottomright);
402                for (int i = 0; i < thickness; i++) {
403                    g.drawLine(i + 1, height - i - 1, width - i - 1, height - i - 1);
404                    g.drawLine(width - i - 1, i, width - i - 1, height - i - 2);
405                    }
406                break;
407
408            case IN:    // Etched Border (in or out)
409            case OUT:
410                int adjust1 = 0;
411                int adjust2 = 0;
412
413                // set font and get info
414                Font oldfont = g.getFont();
415                g.setFont(font);
416                FontMetrics fm = g.getFontMetrics();
417                int ascent = fm.getAscent();
418
419                // set adjustment
420                if (style == IN)
421                    adjust1 = 1;
422                else
423                    adjust2 = 1;
424
425                // Calculate adjustment for text
426                int adjustment = 0;
427                if (text != null && text.length() > 0) {
428                    if (ascent > thickness)
429                        adjustment = (ascent - thickness) / 2;
430                    }
431
432                // The adjustment is there so that we always draw the
433                // light rectangle first. Otherwise, your eye picks up
434                // the discrepancy where the light rect. passes over
435                // the darker rect.
436                int x = thickness / 2;
437                int y = thickness / 2 + adjustment;
438                int w = width - thickness - 1;
439                int h = height - thickness - 1 - adjustment;
440
441                // draw rectangles
442                g.setColor(light);
443                g.drawRect(x + adjust1, y + adjust1, w, h);
444                g.setColor(dark);
445                g.drawRect(x + adjust2, y + adjust2, w, h);
446
447                // draw text, if applicable
448                if (text != null && text.length() > 0) {
449                    // calculate drawing area
450                    int fontheight = fm.getHeight();
451                    int strwidth = fm.stringWidth(text);
452
453                    int textwidth = width - 2 * (thickness + 5);
454                    if (strwidth > textwidth)
455                        strwidth = textwidth;
456
457                    // calculate offset for alignment
458                    int offset;
459                    switch (alignment) {
460                        case CENTER:
461                            offset = (width - strwidth) / 2;
462                            break;
463                        case RIGHT:
464                            offset = width - strwidth - thickness - 5;
465                            break;
466                        case LEFT:
467                        default: // assume left alignment if invalid
468                            offset = thickness + 5;
469                            break;
470                        }
471
472                    // clear drawing area and set clipping region
473                    g.clearRect(offset - 5, 0, strwidth  + 10, fontheight);
474                    g.clipRect(offset, 0, strwidth, fontheight);
475
476                    // draw text
477                    g.setColor(color);
478                    g.drawString(text, offset, ascent);
479
480                    // restore old clipping area
481                    g.clipRect(0, 0, width, height);
482                    }
483
484                g.setFont(oldfont);
485                break;
486
487            case SOLID:
488            default: // assume SOLID
489                g.setColor(color);
490                for (int i = 0; i < thickness; i++)
491                    g.drawRect(i, i, width - 2 * i - 1, height - 2 * i - 1);
492            }
493
494        }
495
496    /**
497     * Returns the settings of this HolidayBorderPanel instance as a string.
498     */
499    public String toString() {
500        StringBuffer str = new StringBuffer("HolidayBorderPanel[");
501
502        // style
503        str.append("style=");
504        switch (style) {
505            case SOLID: str.append("SOLID"); break;
506            case RAISED: str.append("RAISED"); break;
507            case LOWERED: str.append("LOWERED"); break;
508            case IN: str.append("IN"); break;
509            case OUT: str.append("OUT"); break;
510            default: str.append("unknown");
511            }
512        str.append(",");
513
514        // thickness
515        str.append("thickness=");
516        str.append(thickness);
517        str.append(",");
518
519        // gap
520        str.append("gap=");
521        str.append(gap);
522        str.append(",");
523
524        // color
525        str.append(color);
526        str.append(",");
527
528        // font
529        str.append(font);
530        str.append(",");
531
532        // text
533        str.append("text=");
534        str.append(text);
535        str.append(",");
536
537        // alignment
538        str.append("alignment=");
539        switch (alignment) {
540            case LEFT: str.append("LEFT"); break;
541            case CENTER: str.append("CENTER"); break;
542            case RIGHT: str.append("RIGHT"); break;
543            default: str.append("unknown");
544            }
545
546        str.append("]");
547
548        return str.toString();
549        }
550
551    }
552
553