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