1 /*
   2  * $Id$
   3  *
   4  * Copyright (c) 2006, 2016, 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.interview.wizard;
  28 
  29 import java.awt.Component;
  30 import java.awt.Dimension;
  31 import java.awt.GridBagConstraints;
  32 import java.awt.KeyboardFocusManager;
  33 import java.awt.event.ActionEvent;
  34 import java.awt.event.ActionListener;
  35 import java.util.*;
  36 
  37 import javax.accessibility.AccessibleContext;
  38 
  39 import javax.swing.BorderFactory;
  40 import javax.swing.Box;
  41 import javax.swing.BoxLayout;
  42 import javax.swing.JComponent;
  43 import javax.swing.JLabel;
  44 import javax.swing.JPanel;
  45 import javax.swing.JTable;
  46 import javax.swing.JTextArea;
  47 import javax.swing.table.TableCellRenderer;
  48 import javax.swing.table.DefaultTableModel;
  49 import javax.swing.table.TableColumn;
  50 import javax.swing.table.TableModel;
  51 
  52 import com.sun.interview.PropertiesQuestion;
  53 import com.sun.interview.Question;
  54 import com.sun.javatest.tool.UIFactory;
  55 
  56 import javax.swing.CellEditor;
  57 import javax.swing.event.AncestorEvent;
  58 import javax.swing.event.AncestorListener;
  59 import javax.swing.table.TableCellEditor;
  60 
  61 
  62 public class PropertiesQuestionRenderer implements QuestionRenderer {
  63     public JComponent getQuestionRendererComponent(Question qq, ActionListener listener) {
  64         question = (PropertiesQuestion)qq;
  65 
  66         tables = new HashMap<>();
  67 
  68         panel = new JPanel();
  69         panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
  70         panel.setName("properties");
  71         panel.setFocusable(false);
  72 
  73         if (question.getValue() == null) {
  74             showEmptyQuestion(panel);
  75             return panel;
  76         }
  77 
  78         // add table(s)
  79         addGroup(null, panel, listener);
  80 
  81         // note that empty groups are not returned by the next call
  82         String[] groups = question.getGroups();
  83         if (groups != null)
  84             for (int i = 0; i < groups.length; i++) {
  85                 addGroup(groups[i], panel, listener);
  86             }   // for
  87 
  88         if (panel.getComponentCount() == 0) {
  89             showEmptyQuestion(panel);
  90         }
  91 
  92         valueSaver = new Runnable() {
  93                 public void run() {
  94                     Set<String> keys = tables.keySet();
  95                     Iterator<String> iter = keys.iterator();
  96                     while(iter.hasNext()) {
  97                         JTable table = tables.get(iter.next());
  98                         CellEditor editor = table.getCellEditor();
  99                         if(editor != null) {
 100                             editor.stopCellEditing();
 101                         }
 102                     }
 103                 }
 104             };
 105 
 106         panel.putClientProperty(VALUE_SAVER, valueSaver);
 107 
 108 
 109         // This inserted to handle programmatically fired events
 110         // when user click 'X' button in ConfigEditor
 111         panel.addAncestorListener(new AncestorListener() {
 112             public void ancestorAdded(AncestorEvent e) {
 113 
 114             }
 115             public void ancestorMoved(AncestorEvent e) {
 116 
 117             }
 118             public void ancestorRemoved(AncestorEvent e) {
 119                 if (valueSaver != null) {
 120                     valueSaver.run();
 121                 }
 122             }
 123         });
 124 
 125 
 126         return panel;
 127     }
 128 
 129     public String getInvalidValueMessage(Question q) {
 130         return null;
 131     }
 132 
 133     protected void showEmptyQuestion(JPanel panel) {
 134         GridBagConstraints gbc = new GridBagConstraints();
 135         gbc.anchor = GridBagConstraints.PAGE_START;
 136         gbc.fill = GridBagConstraints.BOTH;
 137         gbc.weightx = 2.0;
 138 
 139         // component to hold the message, with lots of 508 adjustments
 140         JTextArea txt = new JTextArea(i18n.getString("props.empty.txt"));
 141         txt.setOpaque(false);
 142         txt.setEditable(false);
 143         txt.setLineWrap(true);
 144         txt.setWrapStyleWord(true);
 145         txt.setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, null);
 146         txt.setFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS, null);
 147         AccessibleContext ac = txt.getAccessibleContext();
 148         ac.setAccessibleName(i18n.getString("props.message.name"));
 149         ac.setAccessibleDescription(i18n.getString("props.message.desc"));
 150 
 151         panel.add(txt, gbc);
 152     }
 153 
 154     // TABLE BUILDING
 155     /**
 156      * @param headers Just an optimization right now.
 157      */
 158     private void addGroup(String group, JPanel panel, ActionListener listener) {
 159         TableModel model = createTableModel(group, listener);
 160         // don't show empty tables
 161         if (model.getRowCount() == 0)
 162             return;
 163 
 164         if (tables.size() == 0) {
 165             Component box = Box.createVerticalStrut(20);
 166             box.setFocusable(false);
 167             panel.add(box);
 168         }
 169 
 170         // null group is for ungrouped properties
 171         if (group != null) {
 172             JLabel label = new JLabel(question.getGroupDisplayName(group));
 173             label.setName(question.getGroupDisplayName(group));
 174             //label.setDisplayedMnemonic(i18n.getString("int.sldr.mne").charAt(0));
 175             //label.setToolTipText(i18n.getString("int.sldr.tip"));
 176             panel.add(label);
 177             Component box = Box.createVerticalStrut(5);
 178             box.setFocusable(false);
 179             panel.add(box);
 180         }
 181         /*
 182         else {
 183             Component box = Box.createVerticalStrut(1);
 184             box.setFocusable(false);
 185             panel.add(box, c);
 186         }
 187         */
 188 
 189         if (renderer == null ||
 190             renderer.getQuestion() != question) {
 191             renderer = new RenderingUtilities.PropCellRenderer(question);
 192         }
 193 
 194         JTable table = createTable(model);
 195         table.setBorder(BorderFactory.createEtchedBorder());
 196         table.setRowSelectionAllowed(false);
 197         table.setColumnSelectionAllowed(false);
 198         table.getTableHeader().setReorderingAllowed(false);
 199         table.setBackground(UIFactory.Colors.WINDOW_BACKGROUND.getValue());
 200 
 201         // setup key column
 202         for (int i = 0; i < table.getColumnCount(); i++) {
 203             TableColumn tc = table.getColumnModel().getColumn(i);
 204             tc.setCellRenderer(getCellRenderer(i));
 205             TableCellEditor editor = getCellEditor(i);
 206             if (editor != null) {
 207                 tc.setCellEditor(editor);
 208             }
 209             tc.setResizable(true);
 210         }
 211 
 212         //panel.add(new JScrollPane(table), c);
 213         panel.add(table.getTableHeader());
 214 
 215         panel.add(table);
 216 
 217         Component box = Box.createVerticalStrut(20);
 218         box.setFocusable(false);
 219         panel.add(box);
 220 
 221         tables.put(group, table);
 222     }
 223 
 224     protected TableCellEditor getCellEditor(int column) {
 225         if (column == 1) {
 226             return new RenderingUtilities.PCE(question);
 227         }
 228         else {
 229             return null;
 230         }
 231     }
 232 
 233     protected TableCellRenderer getCellRenderer(int column) {
 234         return renderer;
 235     }
 236 
 237     protected String[] getTableHeaders() {
 238         return new String[] {question.getKeyHeaderName(),
 239                             question.getValueHeaderName()};
 240     }
 241 
 242     protected TableModel createTableModel(String group, ActionListener l) {
 243         return new PropTableModel(getTableHeaders(), group, question, l);
 244     }
 245 
 246     protected JTable createTable(TableModel model) {
 247         return new PropJTable(model);
 248     }
 249 
 250     // UTILITY
 251 
 252     private void fireEditedEvent(Object src, ActionListener l) {
 253         ActionEvent e = new ActionEvent(src,
 254                                         ActionEvent.ACTION_PERFORMED,
 255                                         EDITED);
 256         l.actionPerformed(e);
 257     }
 258 
 259     protected class PropJTable extends JTable {
 260         protected PropJTable(TableModel model) {
 261             super(model);
 262             setIntercellSpacing(new Dimension(4,4));
 263             setRowHeight((int)(getRowHeight() * 1.5));
 264             setFocusable(false);
 265         }
 266 
 267         public boolean isCellEditable(int row, int column) {
 268             if (column == 0)
 269                 return false;
 270 
 271             if ( column == 1 &&
 272                     question.isReadOnlyValue(question.getKeyPropertyName((String)getValueAt(row, 0))) )
 273                 return false;
 274 
 275             return true;
 276         }
 277     }
 278 
 279     protected class PropTableModel extends DefaultTableModel {
 280         protected PropTableModel(String[] headers, String group, PropertiesQuestion q,
 281                        ActionListener listener) {
 282             super();
 283             this.q = q;
 284             editedListener = listener;
 285 
 286             setColumnCount(headers.length);
 287 
 288             String[][] d = q.getGroup(group);
 289 
 290             if (d != null) {
 291                 ArrayList<String> rm = null;
 292                 for (int i = 0; i < d.length; i++) {
 293                     if (!q.isEntryVisible(d[i][0])) {
 294                         if (rm == null)
 295                             rm = new ArrayList<>();
 296                         else { }
 297                         rm.add(d[i][0]);
 298                     }
 299                     else {
 300                         // this entry is visible
 301                     }
 302                 }   // for
 303 
 304                 // remove items from d
 305                 if (rm != null) {
 306                     String[][] d2 = new String[d.length-rm.size()][2];
 307                     int pos = 0;
 308                     for (int i = 0; i < d.length; i++) {
 309                         if (rm.contains(d[i][0]))
 310                             continue;
 311                         else {
 312                             d2[pos][0] = d[i][0];
 313                             d2[pos][1] = d[i][1];
 314                             pos++;
 315                         }
 316                     // assert: pos == d2.length
 317                     }   // loop should fill d2.length!
 318 
 319                     d = d2;
 320                 }
 321 
 322                 for (int i = 0; i < d.length; i++){
 323                     if (q.getPresentationKeys() != null && q.getPresentationKeys().get(d[i][0]) != null){
 324                         d[i][0] = q.getPresentationKeys().get(d[i][0]);
 325                     }
 326                 }
 327 
 328                 setDataVector(d, headers);
 329             }
 330 
 331             /* old code which doesn't support invisibility
 332             if (d != null)
 333                 setDataVector(d, headers);
 334             */
 335         }
 336 
 337             /*
 338          String getColumnName(int column) {
 339              if (column > headers.length - 1)
 340                  return super.getColumnName();
 341              else
 342                  return headers[column];
 343          }
 344 
 345         String[] headers;
 346          */
 347 
 348         public void setValueAt(Object o, int row, int col) {
 349             if (col == 1) {
 350                 String key = q.getKeyPropertyName((String)(getValueAt(row, 0)));
 351 
 352                 q.updateProperty(key, (String)o);
 353                 fireEditedEvent(this, editedListener);
 354                 fireTableCellUpdated(row, 0);
 355                 fireTableCellUpdated(row, 1);
 356                 o = q.getValue().get(key);
 357             }
 358 
 359             super.setValueAt(o, row, col);
 360         }
 361 
 362         protected PropertiesQuestion q;
 363         protected ActionListener editedListener;
 364     }
 365 
 366     protected Runnable valueSaver;
 367     protected Map<String, JTable> tables;
 368     protected RenderingUtilities.PropCellRenderer renderer;
 369     protected PropertiesQuestion question;
 370     protected JPanel panel;
 371 
 372     private static final I18NResourceBundle i18n = I18NResourceBundle.getDefaultBundle();
 373 }