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