1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * ident "%Z%%M% %I% %E% SMI" 24 * 25 * Copyright (c) 2000 by Sun Microsystems, Inc. 26 * All rights reserved. 27 */ 28 29 /* 30 * Copyright (C) 1996 Active Software, Inc. 31 * All rights reserved. 32 * 33 * @(#) VJPanel.java 1.31 - last change made 08/25/97 34 */ 35 36 package sunsoft.jws.visual.rt.awt; 37 38 import sunsoft.jws.visual.rt.base.*; 39 import java.awt.*; 40 import java.util.Hashtable; 41 42 public class VJPanel extends Panel { 43 // Relief constants 44 public static final int RELIEF_FLAT = Util.RELIEF_FLAT; 45 public static final int RELIEF_RAISED = Util.RELIEF_RAISED; 46 public static final int RELIEF_SUNKEN = Util.RELIEF_SUNKEN; 47 public static final int RELIEF_RIDGE = Util.RELIEF_RIDGE; 48 public static final int RELIEF_GROOVE = Util.RELIEF_GROOVE; 49 public static final int WIN95_RAISED = Util.WIN95_RAISED; 50 public static final int WIN95_SUNKEN = Util.WIN95_SUNKEN; 51 /* BEGIN JSTYLED */ 52 public static final int WIN95_FIELD_BORDER = Util.WIN95_FIELD_BORDER; 53 public static final int WIN95_WINDOW_BORDER = Util.WIN95_WINDOW_BORDER; 54 /* END JSTYLED */ 55 public static final int BLACK_BORDER = Util.BLACK_BORDER; 56 57 // Alignment constants 58 public static final int LEFT = Label.LEFT; 59 public static final int CENTER = Label.CENTER; 60 public static final int RIGHT = Label.RIGHT; 61 62 // Drawing constants 63 private static final int labelpadx = 10; 64 private static final int labelipadx = 4; 65 private static final int labelpadtop = 2; 66 67 // Maker event 68 private static final int MARKER_EVENT = 83250; 69 70 // Multiply factor for darker color 71 private static double FACTOR = 0.85; 72 73 // request focus workaround for Windows 74 static boolean isMouseDown; 75 76 // click count adjustment 77 private static final int CLICK_TIMEOUT = 400; 78 private static final int CLICK_DISTANCE = 2; 79 private static int clickCount = 1; 80 private static long clickTime = 0; 81 private static long clickWhen = -1; 82 private static int clickX, clickY; 83 84 // Relief 85 private int relief; 86 87 // Border width 88 int borderWidth; 89 90 // Label for the upper border of the panel 91 private String borderLabel; 92 93 // Alignment for the borderLabel 94 private int labelAlignment; 95 96 // Insets between the border decoration and the child components 97 private Insets borderInsets; 98 99 // 100 // Event forwarding to groups. 101 // 102 // The MARKER_EVENT stuff is necessary because AWT is broken. For 103 // example, say a key is pressed in a textfield. All of the parents 104 // of the textfield get a chance at the event before the textfield's 105 // peer. If any of the parents returns true from handleEvent, then 106 // the peer never sees the event. 107 // 108 // VJPanel overrides postEvent instead of handleEvent. handleEvent 109 // would have been overridden if it we possible to return true from 110 // handleEvent and not screw up AWT. Since this is not the case, it 111 // becomes necessary to override postEvent instead of handleEvent, 112 // to ensure that all AWT event handling has taken place everywhere 113 // before the event is forwarded to the shadow (and from there to 114 // the group). 115 // 116 // The panel cannot return true from postEvent or else no one 117 // will ever see the event. Therefore, postEvent returns false, 118 // and the MARKER_EVENT stuff ensures that the event doesn't get 119 // delivered twice. 120 // 121 // 122 123 /** 124 * markEvent - Marks events that should be forwarded to the shadow. 125 * 126 * Returns true if the event has been marked, false otherwise. 127 */ markEvent(Event evt, Component comp)128 public static boolean markEvent(Event evt, Component comp) { 129 // 130 // Check for events that are already marked 131 // 132 Event e = evt.evt; 133 while (e != null) { 134 if (e.id == MARKER_EVENT) 135 return false; 136 e = e.evt; 137 } 138 139 // 140 // Figure out the mgr to send the mesage to, and also figure 141 // out the target for the message. 142 // 143 Object messageTarget = null; 144 AttributeManager mgr = null; 145 Hashtable shadowTable = DesignerAccess.getShadowTable(); 146 147 if (evt.target != null) { 148 mgr = (AttributeManager)shadowTable.get(evt.target); 149 messageTarget = mgr; 150 } 151 if (mgr == null) { 152 mgr = (AttributeManager)shadowTable.get(comp); 153 messageTarget = evt.target; 154 } 155 156 // 157 // If we found a mgr, then mark the event and return true. 158 // Otherwise, return false. 159 // 160 if (mgr != null) { 161 Message msg = new Message(messageTarget, /* NOI18N */"AWT", 162 evt, true); 163 164 e = evt; 165 while (e.evt != null) 166 e = e.evt; 167 e.evt = new Event(mgr, MARKER_EVENT, msg); 168 169 e.evt.x = e.x; 170 e.evt.y = e.y; 171 172 return true; 173 } else { 174 return false; 175 } 176 } 177 178 /** 179 * forwardEvent - Forwards marked events to the shadow 180 */ forwardEvent(Event evt, Component comp)181 public static void forwardEvent(Event evt, Component comp) { 182 // Find the marker event and remove it 183 Event p = evt; 184 Event e = evt; 185 while (e.evt != null) { 186 p = e; 187 e = e.evt; 188 } 189 p.evt = null; 190 191 // Make sure we have a marked event 192 if (e.id != MARKER_EVENT) { 193 throw new Error( 194 /* JSTYLED */ 195 Global.fmtMsg("sunsoft.jws.visual.rt.awt.VJPanel.UnmarkedEvent", "forwardEvent")); 196 } 197 198 // Need to untranslate the (x,y) for the event. 199 evt.x = e.x; 200 evt.y = e.y; 201 202 Component target = null; 203 if (evt.target instanceof Component) 204 target = (Component)evt.target; 205 206 while (target != null && target != comp) { 207 Container parent = target.getParent(); 208 if (parent == null) { 209 // We didn't hit comp on the way up the tree, 210 // so don't translate 211 evt.x = e.x; 212 evt.y = e.y; 213 break; 214 } 215 216 translateEvent(evt, target, parent, true); 217 target = parent; 218 } 219 220 // Fix the click count 221 fixClickCount(evt); 222 223 // Send the message 224 AttributeManager mgr = (AttributeManager)e.target; 225 mgr.postMessage((Message)e.arg); 226 } 227 228 // Windows workaround: The location of most 229 // components gets totally 230 // screwed up on Windows. The solution is to 231 // use the location in the 232 // GBConstraints instead. This version of 233 // postEvent translates events 234 // according to the GBConstraints location variable. postEvent(Event e)235 public boolean postEvent(Event e) { 236 // Fix the click count 237 fixClickCount(e); 238 239 if (e.id == Event.MOUSE_DOWN) 240 VJPanel.isMouseDown = true; 241 else if (e.id == Event.MOUSE_UP) 242 VJPanel.isMouseDown = false; 243 244 boolean marked = markEvent(e, this); 245 boolean handled = doPostEvent(e); 246 if (marked) 247 VJPanel.forwardEvent(e, this); 248 249 return handled; 250 } 251 doPostEvent(Event e)252 private boolean doPostEvent(Event e) { 253 boolean handled = false; 254 255 if (Global.isWindows()) { 256 if (handleEvent(e)) { 257 handled = true; 258 } else { 259 Container parent = getParent(); 260 if (parent != null) { 261 translateEvent(e, this, parent); 262 263 if (parent.postEvent(e)) { 264 handled = true; 265 } 266 } 267 } 268 } else { 269 handled = super.postEvent(e); 270 } 271 272 return handled; 273 } 274 275 276 // 277 // This is a workaround for two different problems. 278 // 279 // The first problem is that on Motif, the 280 // click count sometimes does 281 // not work. Sometimes is does sort of work, 282 // but you have to double- 283 // click REALLY fast. This workaround adjusts 284 // the clickCount according 285 // to a reasonable click timeout. 286 // 287 // The second problem is that on Windows you can get double-clicks 288 // even if the second click is at a different x,y 289 // location than the first 290 // click. This workaround makes sure that 291 // if the clicks are far apart, 292 // then it isn't a double click. 293 // 294 fixClickCount(Event evt)295 static void fixClickCount(Event evt) { 296 if (evt.id != Event.MOUSE_DOWN || evt.when == clickWhen) 297 return; 298 299 if (Global.isMotif()) { 300 long curtime = System.currentTimeMillis(); 301 if (evt.when == 0) 302 evt.when = curtime; 303 304 int d = Math.abs(clickX - evt.x) + Math.abs(clickY - evt.y); 305 306 if ((curtime - clickTime) < CLICK_TIMEOUT 307 && (d <= CLICK_DISTANCE)) { 308 clickCount++; 309 evt.clickCount = clickCount; 310 } else { 311 clickCount = 1; 312 } 313 314 if (evt.clickCount == 1) 315 evt.clickCount = clickCount; 316 clickTime = curtime; 317 clickWhen = evt.when; 318 clickX = evt.x; 319 clickY = evt.y; 320 } else if (Global.isWindows()) { 321 long curtime = System.currentTimeMillis(); 322 if (evt.when == 0) 323 evt.when = curtime; 324 325 int d = Math.abs(clickX - evt.x) + Math.abs(clickY - evt.y); 326 327 if (d > CLICK_DISTANCE) { 328 evt.clickCount = 1; 329 } 330 331 clickWhen = evt.when; 332 clickX = evt.x; 333 clickY = evt.y; 334 } 335 } 336 translateEvent(Event e, Component child, Container parent)337 public void translateEvent(Event e, Component child, 338 Container parent) { 339 translateEvent(e, child, parent, false); 340 } 341 translateEvent(Event e, Component child, Container parent, boolean negate)342 private static void translateEvent(Event e, 343 Component child, Container parent, 344 boolean negate) { 345 346 LayoutManager parentMgr = parent.getLayout(); 347 348 // Translate the event using the location 349 // from the GridBagLayout, 350 // if available. This solves the location problem if you use 351 // GridBagLayout for all your containers. 352 if (parentMgr instanceof GBLayout) { 353 GBLayout gb = (GBLayout)parentMgr; 354 GBConstraints c = gb.getConstraints(child); 355 356 Point p = null; 357 if (c != null) 358 p = c.location; 359 if (p == null) 360 p = child.location(); 361 362 if (negate) 363 e.translate(-p.x, -p.y); 364 else 365 e.translate(p.x, p.y); 366 } else { 367 Point p = child.location(); 368 if (negate) 369 e.translate(-p.x, -p.y); 370 else 371 e.translate(p.x, p.y); 372 } 373 } 374 375 // 376 // Constructor 377 // 378 VJPanel()379 public VJPanel() { 380 relief = Util.RELIEF_FLAT; 381 borderLabel = null; 382 borderWidth = 2; 383 borderInsets = new Insets(5, 5, 5, 5); 384 labelAlignment = LEFT; 385 } 386 VJPanel(int relief)387 public VJPanel(int relief) { 388 this(); 389 setRelief(relief); 390 } 391 VJPanel(int relief, String label)392 public VJPanel(int relief, String label) { 393 this(relief); 394 setBorderLabel(label); 395 } 396 VJPanel(int relief, String label, int borderWidth, Insets borderInsets)397 public VJPanel(int relief, String label, 398 int borderWidth, Insets borderInsets) { 399 this(relief, label); 400 setBorderWidth(borderWidth); 401 setBorderInsets(borderInsets); 402 } 403 404 // 405 // Children that are not visible should 406 // not be layed out. The reason 407 // for this is that layout managers ignore non-visible components, 408 // therefore non-visible components do not get reshaped. 409 // 410 // In the case of a non-visible, non-container component, there is 411 // no problem. But if you have a non-visible container, then the 412 // layout method should not be called on that container. But AWT 413 // ignores visibility and calls layout on 414 // all containers regardless. 415 // 416 // The problem with this is that a non-visible container will not 417 // have been reshaped when its parent was layed out. Therefore, 418 // calling layout on this container causes it to do a layout based 419 // on its own bogus size. This means that all the child components 420 // get reshaped and validated with incorrect sizes! If the 421 // non-visible container is later made 422 // visible, all of its components 423 // are already valid so they don't get layed out again. But these 424 // components have incorrect sizes. 425 // 426 // This workaround ensures that layout is not called on non-visible 427 // children of the container. 428 // validate()429 public void validate() { 430 if (!isValid() && getPeer() != null) { 431 layout(); 432 433 // Unfortunately, we don't have access to the valid flag. 434 // Fortunately, it is okay to leave the component invalid. 435 // 436 // Components in an AWT application 437 // are invalid most of the time 438 // anyways, because if any child component invalidate, then 439 // all the parents are invalidated. And there are many 440 // situations where a child calls 441 // invalidate where you don't 442 // want to call validate again. So many components end up 443 // invalid all the time. This is okay, because 444 // it just means 445 // that if you do call validate, then everything will get 446 // layed out again. 447 448 // valid = true; 449 } 450 451 int ncomponents = countComponents(); 452 for (int i = 0; i < ncomponents; i++) { 453 Component comp = getComponent(i); 454 if (!comp.isValid() && comp.getPeer() != 455 null && comp.isVisible()) { 456 comp.validate(); 457 } 458 } 459 } 460 validateTree()461 protected void validateTree() { 462 if (!isValid() && getPeer() != null) { 463 layout(); 464 465 int ncomponents = countComponents(); 466 for (int i = 0; i < ncomponents; ++i) { 467 Component comp = getComponent(i); 468 if ((comp instanceof Container) && 469 !(comp instanceof Window) && 470 (!comp.isValid() && comp.getPeer() != null) && 471 comp.isVisible()) { 472 ((Container)comp).validate(); 473 } 474 } 475 } 476 } 477 478 // Workaround for idiotic JDK1.1 bug where if you add a 479 // component to a container that it is already in, then 480 // the component will be removed from the container! 481 // 482 // #ifdef JDK1.1 addImpl(Component comp, Object constraints, int index)483 protected void addImpl(Component comp, Object constraints, 484 int index) { 485 if (comp.getParent() != this) 486 super.addImpl(comp, constraints, index); 487 } 488 // #endif 489 setRelief(int relief)490 public void setRelief(int relief) { 491 this.relief = relief; 492 493 // Need to invalidate because changing 494 // from a flat relief to something 495 // else will cause the preferredSize to change. 496 invalidate(); 497 repaint(); 498 } 499 getRelief()500 public int getRelief() { 501 return relief; 502 } 503 setBorderWidth(int borderWidth)504 public void setBorderWidth(int borderWidth) { 505 if (borderWidth != this.borderWidth) { 506 this.borderWidth = borderWidth; 507 invalidate(); 508 repaint(); 509 } 510 } 511 getBorderWidth()512 public int getBorderWidth() { 513 return borderWidth; 514 } 515 setBorderLabel(String label)516 public void setBorderLabel(String label) { 517 borderLabel = label; 518 invalidate(); 519 repaint(); 520 } 521 getBorderLabel()522 public String getBorderLabel() { 523 return borderLabel; 524 } 525 setLabelAlignment(int alignment)526 public void setLabelAlignment(int alignment) { 527 labelAlignment = alignment; 528 repaint(); 529 } 530 getLabelAlignment()531 public int getLabelAlignment() { 532 return labelAlignment; 533 } 534 setBorderInsets(Insets insets)535 public void setBorderInsets(Insets insets) { 536 if (insets == null) 537 this.borderInsets = new Insets(0, 0, 0, 0); 538 else 539 this.borderInsets = (Insets)insets.clone(); 540 invalidate(); 541 } 542 getBorderInsets()543 public Insets getBorderInsets() { 544 return (Insets)borderInsets.clone(); 545 } 546 insets()547 public Insets insets() { 548 int bd = getBD(); 549 int h = getLabelAdjustedTop(); 550 Insets insets = getAdjustedInsets(); 551 552 return new Insets(h + insets.top, 553 bd + insets.left, 554 bd + insets.bottom, 555 bd + insets.right); 556 } 557 minimumSize()558 public Dimension minimumSize() { 559 Dimension d = super.minimumSize(); 560 int w = getLabelAdjustedMinWidth(); 561 if (w > d.width) 562 d = new Dimension(w, d.height); 563 return d; 564 } 565 preferredSize()566 public Dimension preferredSize() { 567 Dimension d = super.preferredSize(); 568 int w = getLabelAdjustedMinWidth(); 569 if (w > d.width) 570 d = new Dimension(w, d.height); 571 return d; 572 } 573 getBD()574 private int getBD() { 575 int bd = 0; 576 if (relief != Util.RELIEF_FLAT || borderLabel != null) 577 bd = borderWidth; 578 return bd; 579 } 580 getAdjustedInsets()581 private Insets getAdjustedInsets() { 582 Insets insets; 583 if (relief == Util.RELIEF_FLAT && borderLabel == null) 584 insets = new Insets(0, 0, 0, 0); 585 else 586 insets = borderInsets; 587 return insets; 588 } 589 getLabelAdjustedTop()590 private int getLabelAdjustedTop() { 591 if (relief == Util.RELIEF_FLAT && borderLabel == null) 592 return 0; 593 594 int bd = borderWidth; 595 int top = bd; 596 Font font = getFont(); 597 598 if (borderLabel != null && font != null) { 599 FontMetrics fm = getFontMetrics(font); 600 top = fm.getAscent() + fm.getDescent() + labelpadtop; 601 602 if (!isLabelInBorder()) 603 top += bd; 604 else if (top < bd) 605 top = bd; 606 } 607 608 return top; 609 } 610 getLabelAdjustedMinWidth()611 private int getLabelAdjustedMinWidth() { 612 if (relief == Util.RELIEF_FLAT && borderLabel == null) 613 return 0; 614 615 int bd = borderWidth; 616 int w = 2*bd + borderInsets.left + borderInsets.right; 617 618 Font font = getFont(); 619 if (borderLabel != null && font != null) { 620 FontMetrics fm = getFontMetrics(font); 621 w = Math.max(w, 2*bd + fm.stringWidth(borderLabel) 622 + labelpadx + labelipadx); 623 } 624 625 return w; 626 } 627 isLabelInBorder()628 private boolean isLabelInBorder() { 629 switch (relief) { 630 case Util.RELIEF_RAISED: 631 case Util.RELIEF_SUNKEN: 632 case Util.WIN95_RAISED: 633 case Util.WIN95_SUNKEN: 634 case Util.WIN95_FIELD_BORDER: 635 case Util.WIN95_WINDOW_BORDER: 636 return false; 637 638 case Util.RELIEF_GROOVE: 639 case Util.RELIEF_RIDGE: 640 case Util.BLACK_BORDER: 641 case Util.RELIEF_FLAT: 642 default: 643 return true; 644 } 645 } 646 update(Graphics g)647 public void update(Graphics g) { 648 if (Global.isWindows()) 649 g = getGraphics(); 650 Dimension size = size(); 651 Insets insets = insets(); 652 653 g.setColor(getBackground()); 654 if (insets.left > 0) 655 g.fillRect(0, 0, insets.left, size.height); 656 if (insets.top > 0) 657 g.fillRect(0, 0, size.width, insets.top); 658 if (insets.bottom > 0) 659 g.fillRect(0, size.height-insets.bottom, 660 size.width, insets.bottom); 661 if (insets.right > 0) 662 g.fillRect(size.width-insets.right, 0, 663 insets.right, size.height); 664 665 paint(g); 666 } 667 paint(Graphics g)668 public void paint(Graphics g) { 669 // XXX This workaround is needed for both 670 // Windows and Solaris to ensure 671 // all lightweight components contained 672 // within VJPanel are repainted 673 // correctly; For some reasons, VJPanel 674 // is entirely filled with 675 // the background color whenever update() 676 // is called. If only the 677 // components within the clip region 678 // of the "Graphics g" parameter 679 // is repainted, then the other lightweight 680 // components become 681 // invisible because they get painted 682 // over with the VJPanel background. 683 // The obvious suspect for filling 684 // the VJPanel background is the 685 // fillRect calls in update(), but 686 // the problem exists even when 687 // those fillRect calls are not executed. See bug 4074362. 688 g = getGraphics(); 689 // end of workaround 690 691 super.paint(g); 692 693 Dimension size = size(); 694 Insets insets = insets(); 695 int bd = borderWidth; 696 697 FontMetrics fm = null; 698 if (borderLabel != null) { 699 fm = getFontMetrics(getFont()); 700 } 701 702 // Draw the border 703 if (relief != Util.RELIEF_FLAT && bd > 0) { 704 switch (relief) { 705 case Util.RELIEF_FLAT: 706 case Util.RELIEF_RAISED: 707 case Util.RELIEF_SUNKEN: 708 case Util.RELIEF_RIDGE: 709 case Util.RELIEF_GROOVE: 710 case Util.WIN95_RAISED: 711 case Util.WIN95_SUNKEN: 712 case Util.BLACK_BORDER: 713 g.setColor(getBackground()); 714 break; 715 716 case Util.WIN95_FIELD_BORDER: 717 case Util.WIN95_WINDOW_BORDER: 718 g.setColor(getParent().getBackground()); 719 break; 720 } 721 int yoff = 0; 722 if (borderLabel != null) { 723 int ascent = fm.getAscent(); 724 int descent = fm.getDescent(); 725 726 if (isLabelInBorder()) 727 yoff = (ascent + descent + labelpadtop - bd)/2; 728 else 729 yoff = ascent + descent + labelpadtop; 730 731 if (yoff < 0) 732 yoff = 0; 733 } 734 735 Global.util.draw3DRect(g, 0, yoff, 736 size.width-1, size.height-1-yoff, 737 relief, bd); 738 } 739 740 // Draw the label 741 if (borderLabel != null) { 742 int stringWidth = fm.stringWidth(borderLabel); 743 int ascent = fm.getAscent(); 744 int descent = fm.getDescent(); 745 int x, y, h; 746 747 switch (labelAlignment) { 748 case LEFT: 749 default: 750 x = bd + (labelpadx + labelipadx)/2; 751 break; 752 case CENTER: 753 x = (size.width - stringWidth)/2; 754 break; 755 case RIGHT: 756 x = size.width - (stringWidth + (labelpadx 757 + labelipadx)/2 + bd); 758 break; 759 } 760 761 y = labelpadtop + ascent; 762 h = labelpadtop + ascent + descent; 763 764 if (isLabelInBorder() && bd > h) { 765 y = (bd - h)/2 + (labelpadtop + ascent); 766 h = bd; 767 } 768 769 g.setColor(getBackground()); 770 g.fillRect(x - labelipadx/2, 0, stringWidth + 771 labelipadx, h); 772 773 g.setColor(getForeground()); 774 g.setFont(getFont()); 775 g.drawString(borderLabel, x, y-1); 776 } 777 } 778 779 // 780 // Workaround for Windows95 AWT bug: If you call 781 // request focus while 782 // the mouse is pressed, you get spurious 783 // mouse down events. Not only 784 // that, but the spurious events have 785 // clickCount set to 2, so you end 786 // up with spurious double clicks. On Windows95 the component 787 // automatically gets the focus when you press the mouse inside it. 788 // Therefore, it isn't necessary to call 789 // requestFocus at all if running 790 // on Windows and the mouse is down (and this avoids the bug). 791 // requestFocus()792 public void requestFocus() { 793 if (!Global.isWindows() || !VJPanel.isMouseDown) 794 super.requestFocus(); 795 } 796 } 797