001/*
002 * The contents of this file are subject to the terms of the Common Development and
003 * Distribution License (the License). You may not use this file except in compliance with the
004 * License.
005 *
006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
007 * specific language governing permission and limitations under the License.
008 *
009 * When distributing Covered Software, include this CDDL Header Notice in each file and include
010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
011 * Header, with the fields enclosed by brackets [] replaced by your own identifying
012 * information: "Portions Copyright [year] [name of copyright owner]".
013 *
014 * Copyright 2008 Sun Microsystems, Inc.
015 * Portions Copyright 2014-2016 ForgeRock AS.
016 */
017
018package org.opends.guitools.controlpanel.ui.components;
019
020import static org.opends.messages.AdminToolMessages.*;
021
022import java.awt.GridBagConstraints;
023import java.awt.GridBagLayout;
024import java.awt.event.ActionListener;
025import java.awt.event.KeyEvent;
026import java.text.ParseException;
027
028import javax.swing.Box;
029import javax.swing.Icon;
030import javax.swing.ImageIcon;
031import javax.swing.JButton;
032import javax.swing.JLabel;
033import javax.swing.JPanel;
034import javax.swing.KeyStroke;
035
036import org.forgerock.i18n.LocalizableMessage;
037import org.forgerock.i18n.slf4j.LocalizedLogger;
038import org.opends.guitools.controlpanel.browser.IconPool;
039import org.opends.guitools.controlpanel.datamodel.BinaryValue;
040import org.opends.guitools.controlpanel.ui.ColorAndFontConstants;
041import org.opends.guitools.controlpanel.util.Utilities;
042
043/**
044 * A simple panel used in the LDAP entry viewers to display a binary value.
045 * It does not allow to edit the binary value.  It is used for instance in the
046 * tables.
047 */
048public class BinaryCellPanel extends JPanel
049{
050  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
051  private static final long serialVersionUID = 6607973945986559802L;
052  private static final int THUMBNAIL_HEIGHT = 50;
053
054  private final JButton iconButton;
055  private final JLabel label;
056  private final CellEditorButton editButton;
057  private final CellEditorButton deleteButton;
058  private boolean displayDelete;
059  private final JLabel lockLabel = Utilities.createDefaultLabel();
060
061  private final ImageIcon lockIcon =
062    Utilities.createImageIcon(IconPool.IMAGE_PATH+"/field-locked.png");
063
064  private Object value;
065
066
067  /** Default constructor. */
068  public BinaryCellPanel()
069  {
070    super(new GridBagLayout());
071    setOpaque(false);
072    GridBagConstraints gbc = new GridBagConstraints();
073    gbc.fill = GridBagConstraints.HORIZONTAL;
074    gbc.gridx = 0;
075    gbc.gridy = 0;
076    iconButton = Utilities.createButton(LocalizableMessage.EMPTY);
077    label = Utilities.createDefaultLabel(
078        INFO_CTRL_PANEL_NO_VALUE_SPECIFIED.get());
079    add(iconButton);
080    iconButton.setVisible(false);
081    gbc.weightx = 1.0;
082    gbc.gridx ++;
083    add(label, gbc);
084    add(Box.createHorizontalGlue(), gbc);
085    gbc.gridx ++;
086    editButton = new CellEditorButton(INFO_CTRL_PANEL_EDIT_BUTTON_LABEL.get());
087    editButton.setForeground(ColorAndFontConstants.buttonForeground);
088    editButton.setOpaque(false);
089    gbc.insets.left = 5;
090    gbc.weightx = 0.0;
091    add(editButton, gbc);
092
093    gbc.gridx ++;
094    deleteButton =
095      new CellEditorButton(INFO_CTRL_PANEL_DELETE_BUTTON_LABEL.get());
096    deleteButton.setForeground(ColorAndFontConstants.buttonForeground);
097    deleteButton.setOpaque(false);
098    deleteButton.setVisible(isDisplayDelete());
099    add(deleteButton, gbc);
100
101    gbc.insets.left = 5;
102    gbc.gridx ++;
103    add(lockLabel, gbc);
104    lockLabel.setVisible(false);
105  }
106
107  /**
108   * Returns the message describing the provided array of bytes.
109   * @param value the array of bytes.
110   * @param isImage whether the array of bytes represents an image or not.
111   * @return the message describing the provided array of bytes.
112   */
113  public LocalizableMessage getString(byte[] value, boolean isImage)
114  {
115    if (value == null)
116    {
117      return INFO_CTRL_PANEL_NO_VALUE_SPECIFIED.get();
118    }
119    else if (isImage)
120    {
121      return LocalizableMessage.EMPTY;
122    }
123    else
124    {
125      return INFO_CTRL_PANEL_BINARY_VALUE.get();
126    }
127  }
128
129  /**
130   * Updates the visibility of the lock icon.
131   * @param visible whether the lock icon is visible or not.
132   */
133  public void setLockIconVisible(boolean visible)
134  {
135    if (visible)
136    {
137      lockLabel.setIcon(lockIcon);
138      lockLabel.setVisible(true);
139    }
140    else
141    {
142      lockLabel.setIcon(null);
143      lockLabel.setVisible(false);
144    }
145  }
146
147  /**
148   * Sets the text of the edit button (for instance if this panel is displaying
149   * a read-only value, the user might set a value of 'View...' that launches
150   * a viewer).
151   * @param text the text of the button.
152   */
153  public void setEditButtonText(LocalizableMessage text)
154  {
155    editButton.setText(text.toString());
156  }
157
158  /**
159   * Returns the message describing the provided binary value.
160   * @param value the binary value.
161   * @param isImage whether the binary value represents an image or not.
162   * @return the message describing the provided binary value.
163   */
164  public LocalizableMessage getMessage(BinaryValue value, boolean isImage)
165  {
166    LocalizableMessage returnValue;
167    if (value == null)
168    {
169      returnValue = INFO_CTRL_PANEL_NO_VALUE_SPECIFIED.get();
170    }
171    else if (isImage)
172    {
173      returnValue = LocalizableMessage.EMPTY;
174    }
175    else if (value.getType() == BinaryValue.Type.BASE64_STRING)
176    {
177      returnValue = INFO_CTRL_PANEL_BINARY_VALUE.get();
178    }
179    else
180    {
181      returnValue = INFO_CTRL_PANEL_CONTENTS_OF_FILE.get(value.getFile());
182    }
183    return returnValue;
184  }
185
186  /**
187   * Sets the value to be displayed by this panel.
188   * @param value the binary value as an array of bytes.
189   * @param isImage whether the binary value represents an image or not.
190   */
191  public void setValue(byte[] value, boolean isImage)
192  {
193    label.setText(getString(value, isImage).toString());
194    deleteButton.setVisible(value != null && isDisplayDelete());
195    this.value = value;
196    if (!isImage)
197    {
198      label.setIcon(null);
199      label.setVisible(true);
200      iconButton.setVisible(false);
201    }
202    else
203    {
204      updateIcon(value);
205    }
206  }
207
208  /**
209   * Sets the value to be displayed by this panel.
210   * @param value the binary value as a BinaryValue object.
211   * @param isImage whether the binary value represents an image or not.
212   */
213  public void setValue(BinaryValue value, boolean isImage)
214  {
215    label.setText(getMessage(value, isImage).toString());
216    deleteButton.setVisible(value != null && isDisplayDelete());
217    this.value = value;
218    if (!isImage)
219    {
220      label.setIcon(null);
221      label.setVisible(true);
222      iconButton.setVisible(false);
223    }
224    else
225    {
226      try
227      {
228        updateIcon(value.getBytes());
229      }
230      catch (ParseException pe)
231      {
232        logger.warn(LocalizableMessage.raw("Error decoding base 64 value: "+pe, pe));
233        Utilities.setWarningLabel(label, ERR_LOADING_IMAGE.get());
234      }
235    }
236  }
237
238  private void updateIcon(byte[] value)
239  {
240    if (value == null)
241    {
242      label.setVisible(true);
243      iconButton.setVisible(false);
244    }
245    else
246    {
247      Icon icon = getIcon(value);
248      if (icon == null || icon.getIconHeight() <= 0)
249      {
250        Utilities.setWarningLabel(label, ERR_LOADING_IMAGE.get());
251        label.setVisible(true);
252        iconButton.setVisible(false);
253      }
254      else
255      {
256        iconButton.setVisible(true);
257        iconButton.setIcon(icon);
258        label.setVisible(false);
259      }
260    }
261  }
262
263  /**
264   * Returns the object represented by this panel.
265   * @return the object represented by this panel.
266   */
267  public Object getValue()
268  {
269    return value;
270  }
271
272  /**
273   * Adds an action listener to this panel.  The action listener will be
274   * invoked when the user clicks on the 'Edit' button or the icon.
275   * @param listener the action listener.
276   */
277  public void addEditActionListener(ActionListener listener)
278  {
279    editButton.addActionListener(listener);
280    iconButton.addActionListener(listener);
281  }
282
283  /**
284   * Removes an action listener previously added with the method
285   * addEditActionListener.
286   * @param listener the action listener.
287   */
288  public void removeEditActionListener(ActionListener listener)
289  {
290    editButton.removeActionListener(listener);
291    iconButton.removeActionListener(listener);
292  }
293
294  /**
295   * Adds an action listener to this panel.  The action listener will be
296   * invoked when the user clicks on the 'Delete'.
297   * @param listener the action listener.
298   */
299  public void addDeleteActionListener(ActionListener listener)
300  {
301    deleteButton.addActionListener(listener);
302  }
303
304  /**
305   * Removes an action listener previously added with the method
306   * addDeleteActionListener.
307   * @param listener the action listener.
308   */
309  public void removeDeleteActionListener(ActionListener listener)
310  {
311    deleteButton.removeActionListener(listener);
312  }
313
314  @Override
315  protected boolean processKeyBinding(KeyStroke ks, KeyEvent e,
316      int condition, boolean pressed)
317  {
318    // This method is used to transfer the key events to the button.
319    return editButton.processKeyBinding(ks, e, condition, pressed);
320  }
321
322  /**
323   * Tells whether the 'Delete' button is displayed or not.
324   * @return <CODE>true</CODE> if the 'Delete' button is visible and
325   * <CODE>false</CODE> otherwise.
326   */
327  public boolean isDisplayDelete()
328  {
329    return displayDelete;
330  }
331
332  /**
333   * Sets whether the 'Delete' button must be displayed or not.
334   * @param displayDelete whether the 'Delete' button must be displayed or not.
335   */
336  public void setDisplayDelete(boolean displayDelete)
337  {
338    this.displayDelete = displayDelete;
339  }
340
341  private Icon getIcon(byte[] bytes)
342  {
343    return Utilities.createImageIcon(bytes, THUMBNAIL_HEIGHT,
344        INFO_CTRL_PANEL_THUMBNAIL_DESCRIPTION.get(),
345        true);
346  }
347}