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