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<String, String> map) {
  94         for (Map.Entry<String, String> e : map.entrySet()) {
  95             String key = (e.getKey());
  96             String val = (e.getValue());
  97             if (val != null && !val.trim().isEmpty()) {
  98                 table.updateEntry(key, val);
  99             }
 100         }
 101     }
 102 
 103     protected void updateEntry(String key, String val) {
 104         if (val != null && !val.trim().isEmpty()) {
 105             table.updateEntry(key, val);
 106         }
 107     }
 108 
 109     private Table table;
 110 
 111     private class Table extends JPanel
 112         implements ComponentListener, Scrollable
 113     {
 114         Table(UIFactory uif) {
 115             addComponentListener(this);
 116             setLayout(null);
 117             setBackground(Color.white);
 118             entries = new TreeMap<>();
 119 
 120             // space to go around text
 121             Border bsp = BorderFactory.createEmptyBorder(2, 4, 2, 4); // top, left, bottom, right
 122 
 123             // Border for head components, including head corner above possible scrollbar
 124             // (lines above and below)
 125             Border bh = BorderFactory.createMatteBorder(1, 0, 1, 0, Color.lightGray);
 126             headBorder = BorderFactory.createCompoundBorder(bh, bsp);
 127 
 128             // Border for body components (line below)
 129             Border br = BorderFactory.createMatteBorder(0, 0, 1, 0, Color.lightGray);
 130             bodyBorder = BorderFactory.createCompoundBorder(br, bsp);
 131 
 132             nameLabel = uif.createHeading("test.table.name");
 133             nameLabel.setBorder(headBorder);
 134 
 135             valueLabel = uif.createHeading("test.table.value");
 136             valueLabel.setBorder(headBorder);
 137         }
 138 
 139         void setHead(String nameTitle, String valueTitle) {
 140             nameLabel.setText(nameTitle);
 141             valueLabel.setText(valueTitle);
 142         }
 143 
 144         void updateEntry(String key, String value) {
 145             //System.err.println("TP_PS.Table: updateEntry " + key + "=" + value);
 146             Entry e =  entries.get(key);
 147             if (e == null) {
 148                 e = new Entry(key, value);
 149                 entries.put(key, e);
 150                 maxNameStringWidth = Math.max(maxNameStringWidth, getFontMetrics(getFont()).stringWidth(key));
 151             }
 152             else
 153                 e.valueText.setText(value);
 154 
 155             revalidate();
 156         }
 157 
 158         void reset() {
 159             //System.err.println("TP_PS.Table: reset");
 160             entries.clear();
 161             removeAll();
 162             maxNameStringWidth = 100;
 163             if (!inScrollPane) {
 164                 add(nameLabel);
 165                 add(valueLabel);
 166             }
 167             revalidate();
 168         }
 169 
 170         // JComponent
 171 
 172         public void addNotify() {
 173             super.addNotify();
 174             configureEnclosingScrollPane();
 175         }
 176 
 177         public void removeNotify() {
 178             super.removeNotify();
 179             unconfigureEnclosingScrollPane();
 180         }
 181 
 182         public void revalidate() {
 183             // real revalidate does not work inside scrollpanes ... sigh
 184             // so emulate the necessary behavior instead
 185             //System.err.println("TP_PS.Table: revalidate");
 186             if (inScrollPane) {
 187                 //System.err.println("TP_PS.Table: revalidate inScrollPane");
 188                 synchronized (getTreeLock()) {
 189                     if (pendingValidate == false) {
 190                         //System.err.println("TP_PS.Table: revalidate inScrollPane !valid");
 191                         invalidate();
 192                         SwingUtilities.invokeLater(new Runnable() {
 193                             public void run() {
 194                                 //System.err.println("TP_PS.Table: revalidate callback");
 195                                 synchronized (getTreeLock()) {
 196                                     validate();
 197                                     pendingValidate = false;
 198                                 }
 199                             }
 200                         });
 201                         pendingValidate = true;
 202                     }
 203                 }
 204             }
 205             else
 206                 super.revalidate();
 207         }
 208 
 209         // ComponentListener
 210 
 211         public void componentHidden(ComponentEvent e) {
 212             //System.err.println("TP_PS.Table: componentHidden");
 213         }
 214 
 215         public void componentMoved(ComponentEvent e) {
 216             //System.err.println("TP_PS.Table: componentMoved");
 217         }
 218 
 219         public void componentResized(ComponentEvent e) {
 220             //System.err.println("TP_PS.Table: componentResized " + getSize());
 221             revalidate();
 222         }
 223 
 224         public void componentShown(ComponentEvent e) {
 225             //System.err.println("TP_PS.Table: componentShown");
 226         }
 227 
 228         // Layout
 229 
 230         public void doLayout() {
 231             //System.err.println("TP_PS.Table: doLayout");
 232             synchronized (getTreeLock()) {
 233                 Insets ni = bodyBorder.getBorderInsets(this);
 234                 int nameWidth = ni.left + maxNameStringWidth + 10 + ni.right; // allow padding
 235                 int valueWidth = Math.max(getWidth() - nameWidth, 200);
 236 
 237                 int h = nameLabel.getPreferredSize().height;
 238                 nameLabel.setBounds(0, 0, nameWidth, h);
 239                 valueLabel.setBounds(nameWidth, 0, valueWidth, h);
 240 
 241                 int y = (inScrollPane ? 0 : h);
 242 
 243                 for (Iterator<Entry> iter = entries.values().iterator(); iter.hasNext(); ) {
 244                     Entry e = (iter.next());
 245                     // need to take insets into account for value, since we are dealing
 246                     // with the elemental view inside the valueField
 247                     Insets vi = e.valueText.getInsets();
 248                     View v = e.valueText.getUI().getRootView(e.valueText);
 249                     v.setSize(valueWidth, Integer.MAX_VALUE);
 250                     h = vi.top + ((int) (v.getPreferredSpan(View.Y_AXIS))) + vi.bottom;
 251                     e.nameField.setBounds(0, y, nameWidth, h);
 252                     e.valueText.setBounds(nameWidth, y, valueWidth, h);
 253                     y += h;
 254                 }
 255             }
 256         }
 257 
 258         public Dimension getMinimumSize() {
 259             //System.err.println("TP_PS.Table: minimumLayoutSize");
 260             int h = (inScrollPane ? 0 : nameLabel.getPreferredSize().height);
 261             for (Iterator<Entry> iter = entries.values().iterator(); iter.hasNext(); ) {
 262                 Entry e = (iter.next());
 263                 h += e.valueText.getMinimumSize().height;
 264             }
 265             return new Dimension(maxNameStringWidth + 400, h);
 266         }
 267 
 268         public Dimension getPreferredSize() {
 269             //System.err.println("TP_PS.Table: preferredLayoutSize");
 270             int h = (inScrollPane ? 0 : nameLabel.getPreferredSize().height);
 271             for (Iterator<Entry> iter = entries.values().iterator(); iter.hasNext(); ) {
 272                 Entry e = (iter.next());
 273                 h += e.valueText.getPreferredSize().height;
 274             }
 275             return new Dimension(maxNameStringWidth + 400, h);
 276         }
 277 
 278         // Scrollable
 279 
 280         public Dimension getPreferredScrollableViewportSize() {
 281             return getPreferredSize();
 282         }
 283 
 284         public boolean getScrollableTracksViewportHeight() {
 285             return false;
 286         }
 287 
 288         public boolean getScrollableTracksViewportWidth() {
 289             return true;
 290         }
 291 
 292         public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) {
 293             switch(orientation) {
 294             case SwingConstants.VERTICAL:
 295                 return visibleRect.height / 10;
 296             case SwingConstants.HORIZONTAL:
 297                 return visibleRect.width / 10;
 298             default:
 299                 throw new IllegalArgumentException("Invalid orientation: " + orientation);
 300             }
 301         }
 302 
 303         public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) {
 304             switch(orientation) {
 305             case SwingConstants.VERTICAL:
 306                 return visibleRect.height;
 307             case SwingConstants.HORIZONTAL:
 308                 return visibleRect.width;
 309             default:
 310                 throw new IllegalArgumentException("Invalid orientation: " + orientation);
 311             }
 312         }
 313 
 314         // private
 315 
 316         private void configureEnclosingScrollPane() {
 317             //System.err.println("TP_PS.Table: configureEnclosingScrollPane");
 318             Container p = getParent();
 319             if (p instanceof JViewport) {
 320                 Container gp = p.getParent();
 321                 if (gp instanceof JScrollPane) {
 322                     JScrollPane scrollPane = (JScrollPane)gp;
 323                     // Make certain we are the viewPort's view and not, for
 324                     // example, the rowHeaderView of the scrollPane -
 325                     // an implementor of fixed columns might do this.
 326                     JViewport viewport = scrollPane.getViewport();
 327                     if (viewport == null || viewport.getView() != this)
 328                         return;
 329                     inScrollPane = true;
 330                     scrollPane.setColumnHeaderView(new Header());
 331                     JPanel corner = new JPanel();
 332                     corner.setBackground(Color.white);
 333                     corner.setBorder(headBorder);
 334                     scrollPane.setCorner(JScrollPane.UPPER_RIGHT_CORNER, corner);
 335                 }
 336             }
 337         }
 338 
 339         private void unconfigureEnclosingScrollPane() {
 340             Container p = getParent();
 341             if (p instanceof JViewport) {
 342                 Container gp = p.getParent();
 343                 if (gp instanceof JScrollPane) {
 344                     JScrollPane scrollPane = (JScrollPane)gp;
 345                     // Make certain we are the viewPort's view and not, for
 346                     // example, the rowHeaderView of the scrollPane -
 347                     // an implementor of fixed columns might do this.
 348                     JViewport viewport = scrollPane.getViewport();
 349                     if (viewport == null || viewport.getView() != this)
 350                         return;
 351                     inScrollPane = false;
 352                     scrollPane.setColumnHeaderView(null);
 353                 }
 354             }
 355         }
 356 
 357         private SortedMap<String, Entry> entries;
 358         private int maxNameStringWidth = 100;
 359         private JTextField nameLabel;
 360         private JTextField valueLabel;
 361         private Border headBorder;
 362         private Border bodyBorder;
 363         private boolean inScrollPane;
 364         private boolean pendingValidate;
 365 
 366         private class Entry
 367         {
 368             Entry(String name, String value) {
 369                 this.name = name;
 370                 this.value = value;
 371 
 372                 nameField = uif.createOutputField("test.table.entry.name", name);
 373                 nameField.setBorder(bodyBorder);
 374                 nameField.setEditable(false);
 375                 nameField.setOpaque(false);
 376                 add(nameField);
 377 
 378                 int width = Math.max(getWidth() - maxNameStringWidth, 200);
 379                 valueText = uif.createTextArea("test.table.entry.value");
 380                 valueText.setText(value);
 381                 valueText.setBorder(bodyBorder);
 382                 valueText.setEditable(false);
 383                 valueText.setLineWrap(true);
 384                 add(valueText);
 385             }
 386 
 387             String name;
 388             JTextField nameField;
 389             String value;
 390             JTextArea valueText;
 391         }
 392 
 393         private class Header extends JPanel {
 394             Header() {
 395                 setLayout(null);
 396                 setOpaque(true);
 397                 setBackground(Color.white);
 398                 add(nameLabel);
 399                 add(valueLabel);
 400             }
 401 
 402             public Dimension getMinumumSize() {
 403                 return new Dimension(Table.this.getMinimumSize().width,
 404                                      nameLabel.getMinimumSize().height);
 405             }
 406 
 407             public Dimension getPreferredSize() {
 408                 return new Dimension(Table.this.getPreferredSize().width,
 409                                      nameLabel.getPreferredSize().height);
 410             }
 411 
 412             // doLayout -- nameValue and valueLabel are placed by Table.doLayout
 413         }
 414 
 415     }
 416 
 417 }
 418