org.ut.biolab.medsavant.client.view.component.KeyValuePairPanel.java Source code

Java tutorial

Introduction

Here is the source code for org.ut.biolab.medsavant.client.view.component.KeyValuePairPanel.java

Source

/**
 * See the NOTICE file distributed with this work for additional
 * information regarding copyright ownership.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package org.ut.biolab.medsavant.client.view.component;

import java.awt.*;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.StringSelection;
import java.awt.event.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.swing.*;
import javax.swing.border.Border;
import net.miginfocom.swing.MigLayout;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.ut.biolab.medsavant.client.view.font.FontFactory;

import org.ut.biolab.medsavant.client.view.images.IconFactory;
import org.ut.biolab.medsavant.client.view.util.DialogUtils;
import org.ut.biolab.medsavant.client.view.util.ViewUtil;

/**
 *
 * @author mfiume
 */
public class KeyValuePairPanel extends JPanel {

    public static final String NULL_VALUE = "<NONE>";
    public static final Font KEY_FONT = FontFactory.getGeneralFont().deriveFont(11f).deriveFont(Font.BOLD);
    private Map<String, JLabel> keyKeyComponentMap;
    private Map<String, JPanel> keyValueComponentMap;
    private Map<String, JPanel> keyDetailComponentMap;
    private final Map<String, JPanel[]> keyExtraComponentsMap;
    private final int additionalColumns;
    private final ArrayList<GridBagConstraints> columnConstraints;
    private boolean showingMore = false;
    private List<String> keysInMoreSection;
    private boolean newRowsGoIntoMoreSection;
    private final GridBagConstraints keyDetailConstraints;
    private JPanel kvpPanel;
    private JPanel toolbar;
    private boolean keysVisible = true;
    private static final Log LOG = LogFactory.getLog(KeyValuePairPanel.class);

    public KeyValuePairPanel() {
        this(0);
    }

    public KeyValuePairPanel(int additionalColumns) {
        this(additionalColumns, false);
    }

    @SuppressWarnings("LeakingThisInConstructor")
    public KeyValuePairPanel(int addCols, boolean widelist) {

        setOpaque(false);

        ViewUtil.applyVerticalBoxLayout(this);
        kvpPanel = ViewUtil.getClearPanel();
        toolbar = ViewUtil.getClearPanel();
        ViewUtil.applyHorizontalBoxLayout(toolbar);

        add(kvpPanel);
        add(toolbar);

        additionalColumns = addCols;
        keyKeyComponentMap = new HashMap<String, JLabel>();
        keyValueComponentMap = new HashMap<String, JPanel>();
        keyDetailComponentMap = new HashMap<String, JPanel>();
        keyExtraComponentsMap = new HashMap<String, JPanel[]>();

        columnConstraints = new ArrayList<GridBagConstraints>();
        keysInMoreSection = new ArrayList<String>();

        // Full-width detail component
        keyDetailConstraints = new GridBagConstraints();
        keyDetailConstraints.anchor = GridBagConstraints.SOUTHWEST;
        keyDetailConstraints.weightx = 1.0;
        keyDetailConstraints.fill = GridBagConstraints.BOTH;
        keyDetailConstraints.gridx = 0;
        keyDetailConstraints.gridy = 0;
        keyDetailConstraints.gridwidth = GridBagConstraints.REMAINDER;

        // Constraints for keys
        GridBagConstraints keyConstraints = new GridBagConstraints();
        keyConstraints.anchor = GridBagConstraints.SOUTHWEST;
        keyConstraints.fill = GridBagConstraints.BOTH;
        keyConstraints.weightx = 0;
        keyConstraints.gridx = 0;
        keyConstraints.gridy = 0;
        keyConstraints.ipadx = 5;

        // Constraints for values
        GridBagConstraints valueConstraints = new GridBagConstraints();
        valueConstraints.anchor = GridBagConstraints.SOUTHWEST;
        valueConstraints.fill = GridBagConstraints.BOTH;
        valueConstraints.weightx = widelist ? 0 : 1;
        valueConstraints.gridx = 1;
        valueConstraints.gridy = 0;
        valueConstraints.ipadx = 5;

        columnConstraints.add(keyConstraints);
        columnConstraints.add(valueConstraints);

        // Constraints for additional columns
        for (int i = 0; i < additionalColumns; i++) {
            GridBagConstraints c = new GridBagConstraints();
            c.anchor = GridBagConstraints.WEST;
            c.fill = GridBagConstraints.BOTH;

            // 1 iff widelist and last additional column
            c.weightx = widelist ? ((i == additionalColumns - 1) ? 1 : 0) : 0;
            c.gridx = i + columnConstraints.size();
            c.gridy = 0;
            columnConstraints.add(c);
        }

        GridBagLayout gbl = new GridBagLayout();
        kvpPanel.setLayout(gbl);
    }

    public void setXPadding(int padx) {
        columnConstraints.get(0).ipadx = padx;
        columnConstraints.get(1).ipadx = padx;
    }

    public void setYPadding(int pady) {
        columnConstraints.get(0).ipady = pady;
        columnConstraints.get(1).ipady = pady;
    }

    public static JLabel getKeyLabel(String s) {
        JLabel l = new JLabel(s);
        l.setFont(KEY_FONT);
        return l;
    }

    public JComponent getComponent(String key) {
        JPanel valuePanel = keyValueComponentMap.get(key);
        if (valuePanel.getComponentCount() > 0) {
            return (JComponent) valuePanel.getComponent(0);
        }
        return null;
    }

    public String getValue(String key) {

        JComponent c = getComponent(key);
        if (c instanceof JLabel) {
            // the text of the label may be truncated, use the tooltip instead
            return ((JLabel) c).getToolTipText();
        } else {
            LOG.warn("WARNING: accessing string value of non-string label");
            return c.toString();
        }
    }

    public void addMoreRow() {

        final JToggleButton b = ViewUtil.getSoftToggleButton("MORE");
        ViewUtil.makeMini(b);
        b.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent ae) {
                toggleMoreVisibility();

                if (b.getText().equals("MORE")) {
                    b.setText("LESS");
                } else {
                    b.setText("MORE");
                }
            }
        });

        /*
         * final JLabel keyLabel = new JLabel(); ViewUtil.makeMini(keyLabel);
         * keyLabel.setForeground(Color.darkGray);
         * keyLabel.setBorder(ViewUtil.getMediumBorder());
         *
         * keyLabel.setText(" MORE ");
         *
         * keyLabel.setCursor(new Cursor(Cursor.HAND_CURSOR));
         * keyLabel.addMouseListener(new MouseAdapter() { @Override public void
         * mouseClicked(MouseEvent me) { toggleMoreVisibility();
         *
         * if (keyLabel.getText().startsWith("")) { keyLabel.setText(" MORE
         * "); } else { keyLabel.setText(" LESS "); }
         *
         * }
         * });
         */

        toolbar.add(ViewUtil.getCenterAlignedComponent(b));
        newRowsGoIntoMoreSection = true;

    }

    public void setKeysVisible(boolean b) {
        keysVisible = b;
        resetKeyVisibility();
    }

    private void resetKeyVisibility() {
        for (String key : keyKeyComponentMap.keySet()) {
            keyKeyComponentMap.get(key).setVisible(keysVisible);
        }
    }

    public void toggleMoreVisibility() {
        setMoreVisibility(!showingMore);
    }

    private void setMoreVisibility(boolean b) {
        showingMore = b;
        for (String key : keysInMoreSection) {
            keyKeyComponentMap.get(key).setVisible(b && keysVisible);
            keyValueComponentMap.get(key).setVisible(b);
            for (JComponent c : keyExtraComponentsMap.get(key)) {
                c.setVisible(b);
            }
            // close opened sub components in more if closing more
            if (!b && keyDetailComponentMap.get(key).isVisible()) {
                keyDetailComponentMap.get(key).setVisible(false);
            }
        }
    }

    public void addKey(String key) {
        addKey(key, false);
    }

    public void addKeyWithValue(String key, String value) {
        this.addKey(key);
        this.setValue(key, value);
    }

    public void addKeyWithValue(String key, JComponent value) {
        this.addKey(key);
        this.setValue(key, value);
    }

    public void addKey(final String key, boolean showExpand) {

        Color rowColor = Color.WHITE;
        if (keyKeyComponentMap.size() % 2 == 0) {
            rowColor = ViewUtil.getAlternateRowColor();
        }

        if (newRowsGoIntoMoreSection) {
            keysInMoreSection.add(key);
        }

        rowColor = Color.white;

        String layoutConstraints = "insets 3 3 3 3, filly";

        JPanel valuePanel = ViewUtil.getClearPanel();
        valuePanel.setLayout(new MigLayout(layoutConstraints));
        valuePanel.setBackground(rowColor);

        int i = 0;

        JPanel keyPanel = ViewUtil.getClearPanel();
        keyPanel.setLayout(new MigLayout(layoutConstraints + ", alignx right, hmin 30"));

        keyPanel.setBackground(rowColor);

        final JLabel keyLabel = getKeyLabel(key);

        //keyLabel.setBorder(ViewUtil.getMediumBorder());
        keyKeyComponentMap.put(key, keyLabel);

        if (showExpand) {

            keyLabel.setText("? " + key.toUpperCase());

            keyLabel.setCursor(new Cursor(Cursor.HAND_CURSOR));
            keyLabel.addMouseListener(new MouseAdapter() {
                @Override
                public void mouseClicked(MouseEvent me) {
                    toggleMoreVisibility();

                    if (keyLabel.getText().startsWith("?")) {
                        keyLabel.setText("? " + key.toUpperCase());
                    } else {
                        keyLabel.setText("? " + key.toUpperCase());
                    }
                }
            });
        }

        keyPanel.add(keyLabel);

        keyLabel.setVisible(keysVisible);

        kvpPanel.add(keyPanel, incrementConstraintRow(i++));
        kvpPanel.add(valuePanel, incrementConstraintRow(i++));

        JPanel[] extraComponents = new JPanel[additionalColumns];
        for (int j = 0; j < additionalColumns; j++) {
            JPanel panel = ViewUtil.getClearPanel();
            panel.setBackground(rowColor);
            ViewUtil.applyHorizontalBoxLayout(panel);
            //panel.setBorder(border);
            //panel.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
            extraComponents[j] = panel;//ViewUtil.getClearPanel();
            kvpPanel.add(extraComponents[j], incrementConstraintRow(i++));
        }

        // add hidden panel
        keyDetailConstraints.gridy++;
        keyDetailConstraints.gridy++;

        JPanel detailPanel = ViewUtil.getClearPanel();
        detailPanel.setBackground(rowColor);
        //detailPanel.setBorder(ViewUtil.getTinyLineBorder());
        detailPanel.setVisible(false);
        keyDetailComponentMap.put(key, detailPanel);

        kvpPanel.add(detailPanel, keyDetailConstraints);

        // update all constraints to skip a line
        for (int k = 0; k < 2 + additionalColumns; k++) {
            incrementConstraintRow(k);
        }

        keyValueComponentMap.put(key, valuePanel);
        keyExtraComponentsMap.put(key, extraComponents);

        setMoreVisibility(showingMore);
    }

    public void setValue(String key, JComponent value) {
        JPanel p = keyValueComponentMap.get(key);
        p.removeAll();
        p.add(value);
        p.add(Box.createHorizontalGlue());
        p.repaint();
        p.getParent().repaint();
    }

    /**
     * Set the value for this key to be a
     * <code>JLabel</code> displaying said value. No attempt will be made to
     * split a long tooltip containing comma-delimited values.
     */
    public void setValue(String key, String value) {
        setValue(key, value, false);
    }

    /**
     * Set the value for this key to be a
     * <code>JLabel</code> displaying said value. If
     * <code>splitCommas</code> is true, the value's tooltip will display
     * comma-delimited values on separate rows.
     */
    public void setValue(String key, String value, boolean splitCommas) {

        if (value == null || value.equals(NULL_VALUE)) {
            setValue(key, getNullLabel());
            return;
        }

        if (splitCommas && ((value.indexOf(',') > 0) || value.indexOf(';') > 0)) {
            // For comma-separated and colon-separated lists, we want the text to be multi-line HTML.
            value = "<html>" + value.replace(",", "<br/>").replace(";", "<br/>") + "</html>";

        }

        JLabel c = new JLabel(value);
        if (splitCommas) {
            c.setToolTipText(value);
        }
        setValue(key, c);
    }

    public void setToolTipForValue(String key, String toolTipText) {
        if (keysVisible) {
            keyKeyComponentMap.get(key).setToolTipText(toolTipText);
        } else {
            keyValueComponentMap.get(key).setToolTipText(toolTipText);
        }
    }

    private JLabel getNullLabel() {
        //JPanel p = ViewUtil.getClearPanel();
        JLabel l = new JLabel("NULL");
        l.setForeground(Color.red);
        ViewUtil.makeSmall(l);
        //p.add(ViewUtil.alignLeft(l));
        return l;
    }

    public void setAdditionalColumn(String key, int additionalColumnIndex, Component c) {
        keyExtraComponentsMap.get(key)[additionalColumnIndex].removeAll();
        keyExtraComponentsMap.get(key)[additionalColumnIndex].add(c);
    }

    public GridBagConstraints incrementConstraintRow(int col) {
        GridBagConstraints c = columnConstraints.get(col);
        c.gridy++;
        return c;
    }

    public void toggleDetailVisibility(String key) {
        JPanel p = keyDetailComponentMap.get(key);
        toggleDetailVisibility(key, !p.isVisible());
    }

    public void toggleDetailVisibility(String key, boolean visible) {
        JPanel p = keyDetailComponentMap.get(key);
        p.setVisible(visible);
    }

    public void setDetailComponent(String key, JComponent c) {
        JPanel p = keyDetailComponentMap.get(key);
        p.removeAll();
        p.setLayout(new BorderLayout());
        p.add(c, BorderLayout.CENTER);

    }

    public void setKeyColour(String keyName, Color c) {
        keyKeyComponentMap.get(keyName).setForeground(c);
    }

    //@pre: param key is key of the bottom-most row in kvp
    public void removeBottomRow(String key) {
        if (keysVisible) {
            kvpPanel.remove(keyKeyComponentMap.remove(key));
        }
        kvpPanel.remove(keyValueComponentMap.remove(key));
        for (JPanel j : keyExtraComponentsMap.remove(key)) {
            kvpPanel.remove(j);
        }
        kvpPanel.remove(keyDetailComponentMap.remove(key));
        for (GridBagConstraints c : columnConstraints) {
            c.gridy--;
        }
        keyDetailConstraints.gridy--;
        kvpPanel.invalidate();
        kvpPanel.updateUI();
    }

    public boolean containsKey(String key) {
        return keyKeyComponentMap.containsKey(key);
    }

    public JComponent getAdditionalColumn(String key, int index) {
        JPanel p = keyExtraComponentsMap.get(key)[index];
        if (p.getComponentCount() > 0) {
            return (JComponent) p.getComponent(0);
        }
        return null;
    }

    public static Component getCopyButton(final String key, final KeyValuePairPanel p) {
        JButton button = ViewUtil
                .getTexturedButton(IconFactory.getInstance().getIcon(IconFactory.StandardIcon.COPY));
        button.setToolTipText("Copy " + key);
        button.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent ae) {
                String selection = p.getValue(key);
                StringSelection data = new StringSelection(selection);
                Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
                clipboard.setContents(data, data);
                DialogUtils.displayMessage("Copied \"" + selection + "\" to clipboard.");
            }
        });
        return button;
    }

    public void simpleEllipsify() {
        for (String k : keyValueComponentMap.keySet()) {
            JComponent comp = getComponent(k);
            if (comp instanceof JLabel) {
                JLabel l = (JLabel) comp;
                String s = l.getText();
                if (s.length() > 25) {
                    s = s.substring(0, 20) + "...";
                }
                l.setText(s);
            }

        }
    }

    public void ellipsifyValues(int width) {
        int maxKeyWidth = 0;
        int[] maxAdditionalColumnsWidth = new int[additionalColumns];

        for (JComponent keyComp : keyKeyComponentMap.values()) {
            maxKeyWidth = Math.max(maxKeyWidth, keyComp.getPreferredSize().width);
        }

        for (String k : keyValueComponentMap.keySet()) {
            for (int i = 0; i < additionalColumns; i++) {
                JComponent extraComp = getAdditionalColumn(k, i);
                if (extraComp != null) {
                    maxAdditionalColumnsWidth[i] = Math.max(maxAdditionalColumnsWidth[i],
                            extraComp.getPreferredSize().width);
                }
            }
        }

        width -= maxKeyWidth;
        for (int i : maxAdditionalColumnsWidth) {
            width -= i;
        }

        for (String k : keyValueComponentMap.keySet()) {
            JComponent comp = getComponent(k);
            if (comp != null) {
                int avail = width;
                /*for (int i = 0; i < additionalColumns; i++) {
                 JComponent extraComp = getAdditionalColumn(k, i);
                 if (extraComp != null) {
                 avail -= extraComp.getPreferredSize().width;
                 }
                 }*/

                if (comp instanceof JLabel) {

                    while (avail < comp.getPreferredSize().width) {
                        String text = ((JLabel) comp).getText();
                        if (text.endsWith("")) {
                            if (text.length() > 2) {
                                // Already truncated.
                                text = text.substring(0, text.length() - 2);
                            } else {
                                break; // As short as we can get.  Can't truncate any more.
                            }
                        } else {
                            // Reasonable first truncation is to trim off the last word.
                            int spacePos = text.lastIndexOf(' ');
                            if (spacePos > 0) {
                                text = text.substring(0, spacePos);
                            } else {
                                FontMetrics fm = comp.getFontMetrics(comp.getFont());

                                while (fm.stringWidth(text + "") > avail) {
                                    //causes StringIndexOutOfBoundsException if text is empty.  
                                    if (text == null || text.length() < 2) {
                                        LOG.info("Text is null or empty in KeyValuePairPanel");
                                        break;
                                    }
                                    text = text.substring(0, text.length() - 2);
                                }
                                //text = text + "";

                                //text = text.substring(0, text.length() - 1);
                            }
                        }
                        ((JLabel) comp).setText(text + "");
                    }
                } else {
                    // Can't truncate, but we can force the preferred size.
                    comp.setMaximumSize(new Dimension(avail, comp.getPreferredSize().height));
                }
            }
        }
    }
}