1 /*
   2  * $Id$
   3  *
   4  * Copyright (c) 2001, 2009, Oracle and/or its affiliates. All rights reserved.
   5  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   6  *
   7  * This code is free software; you can redistribute it and/or modify it
   8  * under the terms of the GNU General Public License version 2 only, as
   9  * published by the Free Software Foundation.  Oracle designates this
  10  * particular file as subject to the "Classpath" exception as provided
  11  * by Oracle in the LICENSE file that accompanied this code.
  12  *
  13  * This code is distributed in the hope that it will be useful, but WITHOUT
  14  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  15  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  16  * version 2 for more details (a copy is included in the LICENSE file that
  17  * accompanied this code).
  18  *
  19  * You should have received a copy of the GNU General Public License version
  20  * 2 along with this work; if not, write to the Free Software Foundation,
  21  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  22  *
  23  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  24  * or visit www.oracle.com if you need additional information or have any
  25  * questions.
  26  */
  27 package com.sun.javatest.exec;
  28 
  29 import java.awt.BorderLayout;
  30 import java.awt.Color;
  31 import java.awt.Container;
  32 import java.awt.Dimension;
  33 import java.awt.Insets;
  34 import java.awt.Point;
  35 import java.awt.Rectangle;
  36 import java.awt.event.ComponentEvent;
  37 import java.awt.event.ComponentListener;
  38 import java.util.Iterator;
  39 import java.util.Map;
  40 import java.util.SortedMap;
  41 import java.util.TreeMap;
  42 
  43 import javax.swing.BorderFactory;
  44 import javax.swing.JPanel;
  45 import javax.swing.JScrollPane;
  46 import javax.swing.JTextArea;
  47 import javax.swing.JTextField;
  48 import javax.swing.JViewport;
  49 import javax.swing.Scrollable;
  50 import javax.swing.SwingConstants;
  51 import javax.swing.SwingUtilities;
  52 import javax.swing.border.Border;
  53 import javax.swing.text.View;
  54 
  55 import com.sun.javatest.TestResult;
  56 import com.sun.javatest.tool.UIFactory;
  57 
  58 /**
  59  * Base class of all subpanels of TestPanel that need to display property list
  60  * type information.
  61  */
  62 
  63 abstract class TP_PropertySubpanel
  64     extends TP_Subpanel
  65 {
  66     protected TP_PropertySubpanel(UIFactory uif, String uiKey) {
  67         super(uif, uiKey);
  68         setLayout(new BorderLayout());
  69         setOpaque(false);
  70 
  71         JTextField caption = uif.createHeading("test." + uiKey + ".caption");
  72         caption.setBorder(BorderFactory.createEmptyBorder(5, 10, 5, 10));
  73         add(caption, BorderLayout.NORTH);
  74 
  75         table = new Table(uif);
  76 
  77         JScrollPane sp = uif.createScrollPane(table);
  78         sp.getViewport().setViewPosition(new Point(0, 0));
  79         sp.getViewport().setBackground(Color.white);
  80         add(sp, BorderLayout.CENTER);
  81     }
  82 
  83     void setHead(String nameTitle, String valueTitle) {
  84         table.setHead(nameTitle, valueTitle);
  85     }
  86 
  87     protected void updateSubpanel(TestResult currTest) {
  88         //System.err.println("TP_PS: updateSubpanel");
  89         super.updateSubpanel(currTest);
  90         table.reset();
  91     }
  92 
  93     protected void updateEntries(Map map) {
  94         for (Iterator i = map.entrySet().iterator(); i.hasNext(); ) {
  95             Map.Entry e = (Map.Entry) (i.next());
  96             String key = (String) (e.getKey());
  97             String val = (String) (e.getValue());
  98             if (val != null && !val.trim().isEmpty()) {
  99                 table.updateEntry(key, val);
 100             }
 101         }
 102     }
 103 
 104     protected void updateEntry(String key, String val) {
 105         if (val != null && !val.trim().isEmpty()) {
 106             table.updateEntry(key, val);
 107         }
 108     }
 109 
 110     private Table table;
 111 
 112     private class Table extends JPanel
 113         implements ComponentListener, Scrollable
 114     {
 115         Table(UIFactory uif) {
 116             addComponentListener(this);
 117             setLayout(null);
 118             setBackground(Color.white);
 119             entries = new TreeMap<>();
 120 
 121             // space to go around text
 122             Border bsp = BorderFactory.createEmptyBorder(2, 4, 2, 4); // top, left, bottom, right
 123 
 124             // Border for head components, including head corner above possible scrollbar
 125             // (lines above and below)
 126             Border bh = BorderFactory.createMatteBorder(1, 0, 1, 0, Color.lightGray);
 127             headBorder = BorderFactory.createCompoundBorder(bh, bsp);
 128 
 129             // Border for body components (line below)
 130             Border br = BorderFactory.createMatteBorder(0, 0, 1, 0, Color.lightGray);
 131             bodyBorder = BorderFactory.createCompoundBorder(br, bsp);
 132 
 133             nameLabel = uif.createHeading("test.table.name");
 134             nameLabel.setBorder(headBorder);
 135 
 136             valueLabel = uif.createHeading("test.table.value");
 137             valueLabel.setBorder(headBorder);
 138         }
 139 
 140         void setHead(String nameTitle, String valueTitle) {
 141             nameLabel.setText(nameTitle);
 142             valueLabel.setText(valueTitle);
 143         }
 144 
 145         void updateEntry(String key, String value) {
 146             //System.err.println("TP_PS.Table: updateEntry " + key + "=" + value);
 147             Entry e =  entries.get(key);
 148             if (e == null) {
 149                 e = new Entry(key, value);
 150                 entries.put(key, e);
 151                 maxNameStringWidth = Math.max(maxNameStringWidth, getFontMetrics(getFont()).stringWidth(key));
 152             }
 153             else
 154                 e.valueText.setText(value);
 155 
 156             revalidate();
 157         }
 158 
 159         void reset() {
 160             //System.err.println("TP_PS.Table: reset");
 161             entries.clear();
 162             removeAll();
 163             maxNameStringWidth = 100;
 164             if (!inScrollPane) {
 165                 add(nameLabel);
 166                 add(valueLabel);
 167             }
 168             revalidate();
 169         }
 170 
 171         // JComponent
 172 
 173         public void addNotify() {
 174             super.addNotify();
 175             configureEnclosingScrollPane();
 176         }
 177 
 178         public void removeNotify() {
 179             super.removeNotify();
 180             unconfigureEnclosingScrollPane();
 181         }
 182 
 183         public void revalidate() {
 184             // real revalidate does not work inside scrollpanes ... sigh
 185             // so emulate the necessary behavior instead
 186             //System.err.println("TP_PS.Table: revalidate");
 187             if (inScrollPane) {
 188                 //System.err.println("TP_PS.Table: revalidate inScrollPane");
 189                 synchronized (getTreeLock()) {
 190                     if (pendingValidate == false) {
 191                         //System.err.println("TP_PS.Table: revalidate inScrollPane !valid");
 192                         invalidate();
 193                         SwingUtilities.invokeLater(new Runnable() {
 194                             public void run() {
 195                                 //System.err.println("TP_PS.Table: revalidate callback");
 196                                 synchronized (getTreeLock()) {
 197                                     validate();
 198                                     pendingValidate = false;
 199                                 }
 200                             }
 201                         });
 202                         pendingValidate = true;
 203                     }
 204                 }
 205             }
 206             else
 207                 super.revalidate();
 208         }
 209 
 210         // ComponentListener
 211 
 212         public void componentHidden(ComponentEvent e) {
 213             //System.err.println("TP_PS.Table: componentHidden");
 214         }
 215 
 216         public void componentMoved(ComponentEvent e) {
 217             //System.err.println("TP_PS.Table: componentMoved");
 218         }
 219 
 220         public void componentResized(ComponentEvent e) {
 221             //System.err.println("TP_PS.Table: componentResized " + getSize());
 222             revalidate();
 223         }
 224 
 225         public void componentShown(ComponentEvent e) {
 226             //System.err.println("TP_PS.Table: componentShown");
 227         }
 228 
 229         // Layout
 230 
 231         public void doLayout() {
 232             //System.err.println("TP_PS.Table: doLayout");
 233             synchronized (getTreeLock()) {
 234                 Insets ni = bodyBorder.getBorderInsets(this);
 235                 int nameWidth = ni.left + maxNameStringWidth + 10 + ni.right; // allow padding
 236                 int valueWidth = Math.max(getWidth() - nameWidth, 200);
 237 
 238                 int h = nameLabel.getPreferredSize().height;
 239                 nameLabel.setBounds(0, 0, nameWidth, h);
 240                 valueLabel.setBounds(nameWidth, 0, valueWidth, h);
 241 
 242                 int y = (inScrollPane ? 0 : h);
 243 
 244                 for (Iterator iter = entries.values().iterator(); iter.hasNext(); ) {
 245                     Entry e = (Entry) (iter.next());
 246                     // need to take insets into account for value, since we are dealing
 247                     // with the elemental view inside the valueField
 248                     Insets vi = e.valueText.getInsets();
 249                     View v = e.valueText.getUI().getRootView(e.valueText);
 250                     v.setSize(valueWidth, Integer.MAX_VALUE);
 251                     h = vi.top + ((int) (v.getPreferredSpan(View.Y_AXIS))) + vi.bottom;
 252                     e.nameField.setBounds(0, y, nameWidth, h);
 253                     e.valueText.setBounds(nameWidth, y, valueWidth, h);
 254                     y += h;
 255                 }
 256             }
 257         }
 258 
 259         public Dimension getMinimumSize() {
 260             //System.err.println("TP_PS.Table: minimumLayoutSize");
 261             int h = (inScrollPane ? 0 : nameLabel.getPreferredSize().height);
 262             for (Iterator iter = entries.values().iterator(); iter.hasNext(); ) {
 263                 Entry e = (Entry) (iter.next());
 264                 h += e.valueText.getMinimumSize().height;
 265             }
 266             return new Dimension(maxNameStringWidth + 400, h);
 267         }
 268 
 269         public Dimension getPreferredSize() {
 270             //System.err.println("TP_PS.Table: preferredLayoutSize");
 271             int h = (inScrollPane ? 0 : nameLabel.getPreferredSize().height);
 272             for (Iterator iter = entries.values().iterator(); iter.hasNext(); ) {
 273                 Entry e = (Entry) (iter.next());
 274                 h += e.valueText.getPreferredSize().height;
 275             }
 276             return new Dimension(maxNameStringWidth + 400, h);
 277         }
 278 
 279         // Scrollable
 280 
 281         public Dimension getPreferredScrollableViewportSize() {
 282             return getPreferredSize();
 283         }
 284 
 285         public boolean getScrollableTracksViewportHeight() {
 286             return false;
 287         }
 288 
 289         public boolean getScrollableTracksViewportWidth() {
 290             return true;
 291         }
 292 
 293         public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) {
 294             switch(orientation) {
 295             case SwingConstants.VERTICAL:
 296                 return visibleRect.height / 10;
 297             case SwingConstants.HORIZONTAL:
 298                 return visibleRect.width / 10;
 299             default:
 300                 throw new IllegalArgumentException("Invalid orientation: " + orientation);
 301             }
 302         }
 303 
 304         public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) {
 305             switch(orientation) {
 306             case SwingConstants.VERTICAL:
 307                 return visibleRect.height;
 308             case SwingConstants.HORIZONTAL:
 309                 return visibleRect.width;
 310             default:
 311                 throw new IllegalArgumentException("Invalid orientation: " + orientation);
 312             }
 313         }
 314 
 315         // private
 316 
 317         private void configureEnclosingScrollPane() {
 318             //System.err.println("TP_PS.Table: configureEnclosingScrollPane");
 319             Container p = getParent();
 320             if (p instanceof JViewport) {
 321                 Container gp = p.getParent();
 322                 if (gp instanceof JScrollPane) {
 323                     JScrollPane scrollPane = (JScrollPane)gp;
 324                     // Make certain we are the viewPort's view and not, for
 325                     // example, the rowHeaderView of the scrollPane -
 326                     // an implementor of fixed columns might do this.
 327                     JViewport viewport = scrollPane.getViewport();
 328                     if (viewport == null || viewport.getView() != this)
 329                         return;
 330                     inScrollPane = true;
 331                     scrollPane.setColumnHeaderView(new Header());
 332                     JPanel corner = new JPanel();
 333                     corner.setBackground(Color.white);
 334                     corner.setBorder(headBorder);
 335                     scrollPane.setCorner(JScrollPane.UPPER_RIGHT_CORNER, corner);
 336                 }
 337             }
 338         }
 339 
 340         private void unconfigureEnclosingScrollPane() {
 341             Container p = getParent();
 342             if (p instanceof JViewport) {
 343                 Container gp = p.getParent();
 344                 if (gp instanceof JScrollPane) {
 345                     JScrollPane scrollPane = (JScrollPane)gp;
 346                     // Make certain we are the viewPort's view and not, for
 347                     // example, the rowHeaderView of the scrollPane -
 348                     // an implementor of fixed columns might do this.
 349                     JViewport viewport = scrollPane.getViewport();
 350                     if (viewport == null || viewport.getView() != this)
 351                         return;
 352                     inScrollPane = false;
 353                     scrollPane.setColumnHeaderView(null);
 354                 }
 355             }
 356         }
 357 
 358         private SortedMap<String, Entry> entries;
 359         private int maxNameStringWidth = 100;
 360         private JTextField nameLabel;
 361         private JTextField valueLabel;
 362         private Border headBorder;
 363         private Border bodyBorder;
 364         private boolean inScrollPane;
 365         private boolean pendingValidate;
 366 
 367         private class Entry
 368         {
 369             Entry(String name, String value) {
 370                 this.name = name;
 371                 this.value = value;
 372 
 373                 nameField = uif.createOutputField("test.table.entry.name", name);
 374                 nameField.setBorder(bodyBorder);
 375                 nameField.setEditable(false);
 376                 nameField.setOpaque(false);
 377                 add(nameField);
 378 
 379                 int width = Math.max(getWidth() - maxNameStringWidth, 200);
 380                 valueText = uif.createTextArea("test.table.entry.value");
 381                 valueText.setText(value);
 382                 valueText.setBorder(bodyBorder);
 383                 valueText.setEditable(false);
 384                 valueText.setLineWrap(true);
 385                 add(valueText);
 386             }
 387 
 388             String name;
 389             JTextField nameField;
 390             String value;
 391             JTextArea valueText;
 392         }
 393 
 394         private class Header extends JPanel {
 395             Header() {
 396                 setLayout(null);
 397                 setOpaque(true);
 398                 setBackground(Color.white);
 399                 add(nameLabel);
 400                 add(valueLabel);
 401             }
 402 
 403             public Dimension getMinumumSize() {
 404                 return new Dimension(Table.this.getMinimumSize().width,
 405                                      nameLabel.getMinimumSize().height);
 406             }
 407 
 408             public Dimension getPreferredSize() {
 409                 return new Dimension(Table.this.getPreferredSize().width,
 410                                      nameLabel.getPreferredSize().height);
 411             }
 412 
 413             // doLayout -- nameValue and valueLabel are placed by Table.doLayout
 414         }
 415 
 416     }
 417 
 418 }
 419