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