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-2009 Sun Microsystems, Inc.
025 *      Portions Copyright 2015 ForgeRock AS.
026 */
027
028package org.opends.guitools.controlpanel.ui.components;
029
030import static org.opends.messages.AdminToolMessages.*;
031
032import java.awt.Component;
033import java.awt.Dimension;
034import java.awt.GridBagConstraints;
035import java.awt.GridBagLayout;
036import java.awt.Insets;
037import java.awt.event.ActionEvent;
038import java.awt.event.ActionListener;
039import java.awt.event.MouseAdapter;
040import java.awt.event.MouseEvent;
041
042import javax.swing.Box;
043import javax.swing.JButton;
044import javax.swing.JLabel;
045import javax.swing.JList;
046import javax.swing.JPanel;
047import javax.swing.JScrollPane;
048import javax.swing.ListSelectionModel;
049import javax.swing.event.ListDataEvent;
050import javax.swing.event.ListDataListener;
051import javax.swing.event.ListSelectionEvent;
052import javax.swing.event.ListSelectionListener;
053
054import org.opends.guitools.controlpanel.datamodel.SortableListModel;
055import org.opends.guitools.controlpanel.util.Utilities;
056
057/**
058 * This component displays two list (available list and selected list) with
059 * some buttons to move the components of one list to the other.
060 *
061 * @param <T> the type of the objects in the list.
062 */
063public class AddRemovePanel<T> extends JPanel
064{
065  private static final long serialVersionUID = 461800576153651284L;
066  private SortableListModel<T> availableListModel;
067  private SortableListModel<T> selectedListModel;
068  private JLabel selectedLabel;
069  private JLabel availableLabel;
070  private JButton add;
071  private JButton remove;
072  private JButton addAll;
073  private JButton removeAll;
074  private JScrollPane availableScroll;
075  private JScrollPane selectedScroll;
076  private JList availableList;
077  private JList selectedList;
078  private Class<T> theClass;
079
080  /**
081   * Mask used as display option.  If the provided display options contain
082   * this mask, the panel will display the remove all button.
083   */
084  public static final int DISPLAY_REMOVE_ALL = 0x001;
085
086  /**
087   * Mask used as display option.  If the provided display options contain
088   * this mask, the panel will display the add all button.
089   */
090  public static final int DISPLAY_ADD_ALL = 0x010;
091
092
093  /**
094   * Constructor of the default add remove panel (including 'Add All' and
095   * 'Remove All' buttons).
096   * The class is required to avoid warnings in compilation.
097   * @param theClass the class of the objects in the panel.
098   */
099  public AddRemovePanel(Class<T> theClass)
100  {
101    this(DISPLAY_REMOVE_ALL | DISPLAY_ADD_ALL, theClass);
102  }
103
104  /**
105   * Constructor of the add remove panel allowing the user to provide some
106   * display options.
107   * The class is required to avoid warnings in compilation.
108   * @param displayOptions the display options.
109   * @param theClass the class of the objects in the panel.
110   */
111  public AddRemovePanel(int displayOptions, Class<T> theClass)
112  {
113    super(new GridBagLayout());
114    setOpaque(false);
115    this.theClass = theClass;
116    GridBagConstraints gbc = new GridBagConstraints();
117    gbc.gridx = 0;
118    gbc.gridy = 0;
119    gbc.weightx = 0.0;
120    gbc.weighty = 0.0;
121    gbc.gridwidth = 1;
122    gbc.gridheight = 1;
123    gbc.fill = GridBagConstraints.HORIZONTAL;
124    gbc.anchor = GridBagConstraints.WEST;
125
126    availableLabel = Utilities.createDefaultLabel(
127        INFO_CTRL_PANEL_AVAILABLE_LABEL.get());
128    add(availableLabel, gbc);
129    gbc.gridx = 2;
130    selectedLabel = Utilities.createDefaultLabel(
131        INFO_CTRL_PANEL_SELECTED_LABEL.get());
132    add(selectedLabel, gbc);
133    gbc.gridy ++;
134
135    ListDataListener listDataListener = new ListDataListener()
136    {
137      /** {@inheritDoc} */
138      public void intervalRemoved(ListDataEvent ev)
139      {
140        updateButtonEnabling();
141      }
142
143      /** {@inheritDoc} */
144      public void intervalAdded(ListDataEvent ev)
145      {
146        updateButtonEnabling();
147      }
148
149      /** {@inheritDoc} */
150      public void contentsChanged(ListDataEvent ev)
151      {
152        updateButtonEnabling();
153      }
154    };
155    MouseAdapter doubleClickListener = new MouseAdapter()
156    {
157      /** {@inheritDoc} */
158      public void mouseClicked(MouseEvent e) {
159        if (isEnabled() && e.getClickCount() == 2)
160        {
161          if (e.getSource() == availableList)
162          {
163            if (availableList.getSelectedValue() != null)
164            {
165              addClicked();
166            }
167          }
168          else if (e.getSource() == selectedList)
169          {
170            if (selectedList.getSelectedValue() != null)
171            {
172              removeClicked();
173            }
174          }
175        }
176      }
177    };
178
179
180    availableListModel = new SortableListModel<>();
181    availableListModel.addListDataListener(listDataListener);
182    availableList = new JList();
183    availableList.setModel(availableListModel);
184    availableList.setVisibleRowCount(15);
185    availableList.addMouseListener(doubleClickListener);
186
187    selectedListModel = new SortableListModel<>();
188    selectedListModel.addListDataListener(listDataListener);
189    selectedList = new JList();
190    selectedList.setModel(selectedListModel);
191    selectedList.setVisibleRowCount(15);
192    selectedList.addMouseListener(doubleClickListener);
193
194    gbc.weighty = 1.0;
195    gbc.weightx = 1.0;
196    gbc.gridheight = 3;
197    if ((displayOptions & DISPLAY_ADD_ALL) != 0)
198    {
199      gbc.gridheight ++;
200    }
201    if ((displayOptions & DISPLAY_REMOVE_ALL) != 0)
202    {
203      gbc.gridheight ++;
204    }
205    int listGridHeight = gbc.gridheight;
206    int listGridY = gbc.gridy;
207    gbc.gridx = 0;
208    gbc.insets.top = 5;
209    availableScroll = Utilities.createScrollPane(availableList);
210    gbc.fill = GridBagConstraints.BOTH;
211    add(availableScroll, gbc);
212
213    gbc.gridx = 1;
214    gbc.gridheight = 1;
215    gbc.weightx = 0.0;
216    gbc.weighty = 0.0;
217    gbc.fill = GridBagConstraints.HORIZONTAL;
218    add = Utilities.createButton(INFO_CTRL_PANEL_ADDREMOVE_ADD_BUTTON.get());
219    add.setOpaque(false);
220    add.addActionListener(new ActionListener()
221    {
222      /** {@inheritDoc} */
223      public void actionPerformed(ActionEvent ev)
224      {
225        addClicked();
226      }
227    });
228    gbc.insets = new Insets(5, 5, 0, 5);
229    add(add, gbc);
230
231    if ((displayOptions & DISPLAY_ADD_ALL) != 0)
232    {
233      addAll = Utilities.createButton(
234          INFO_CTRL_PANEL_ADDREMOVE_ADD_ALL_BUTTON.get());
235      addAll.setOpaque(false);
236      addAll.addActionListener(new ActionListener()
237      {
238        /** {@inheritDoc} */
239        public void actionPerformed(ActionEvent ev)
240        {
241          selectedListModel.addAll(availableListModel.getData());
242          availableListModel.clear();
243          selectedListModel.fireContentsChanged(selectedListModel, 0,
244              selectedListModel.getSize());
245          availableListModel.fireContentsChanged(availableListModel, 0,
246              availableListModel.getSize());
247        }
248      });
249      gbc.gridy ++;
250      add(addAll, gbc);
251    }
252
253    remove = Utilities.createButton(
254        INFO_CTRL_PANEL_ADDREMOVE_REMOVE_BUTTON.get());
255    remove.setOpaque(false);
256    remove.addActionListener(new ActionListener()
257    {
258      /** {@inheritDoc} */
259      public void actionPerformed(ActionEvent ev)
260      {
261        removeClicked();
262      }
263    });
264    gbc.gridy ++;
265    gbc.insets.top = 10;
266    add(remove, gbc);
267
268    if ((displayOptions & DISPLAY_REMOVE_ALL) != 0)
269    {
270      removeAll = Utilities.createButton(
271          INFO_CTRL_PANEL_ADDREMOVE_REMOVE_ALL_BUTTON.get());
272      removeAll.setOpaque(false);
273      removeAll.addActionListener(new ActionListener()
274      {
275        /** {@inheritDoc} */
276        public void actionPerformed(ActionEvent ev)
277        {
278          availableListModel.addAll(selectedListModel.getData());
279          selectedListModel.clear();
280          selectedListModel.fireContentsChanged(selectedListModel, 0,
281              selectedListModel.getSize());
282          availableListModel.fireContentsChanged(availableListModel, 0,
283              availableListModel.getSize());
284        }
285      });
286      gbc.gridy ++;
287      gbc.insets.top = 5;
288      add(removeAll, gbc);
289    }
290
291
292    gbc.weighty = 1.0;
293    gbc.insets = new Insets(0, 0, 0, 0);
294    gbc.gridy ++;
295    gbc.fill = GridBagConstraints.VERTICAL;
296    add(Box.createVerticalGlue(), gbc);
297
298    gbc.weightx = 1.0;
299    gbc.insets = new Insets(5, 0, 0, 0);
300    gbc.gridheight = listGridHeight;
301    gbc.gridy = listGridY;
302    gbc.gridx = 2;
303    gbc.fill = GridBagConstraints.BOTH;
304    selectedScroll = Utilities.createScrollPane(selectedList);
305    add(selectedScroll, gbc);
306
307    selectedList.getSelectionModel().setSelectionMode(
308        ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
309    ListSelectionListener listener = new ListSelectionListener()
310    {
311      public void valueChanged(ListSelectionEvent ev)
312      {
313        updateButtonEnabling();
314      }
315    };
316    selectedList.getSelectionModel().addListSelectionListener(listener);
317    availableList.getSelectionModel().setSelectionMode(
318        ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
319    availableList.getSelectionModel().addListSelectionListener(listener);
320
321    add.setEnabled(false);
322    remove.setEnabled(false);
323
324    // Set preferred size for the scroll panes.
325    Component comp =
326      availableList.getCellRenderer().getListCellRendererComponent(
327          availableList,
328        "The cell that we want to display", 0, true, true);
329    Dimension d = new Dimension(comp.getPreferredSize().width,
330        availableScroll.getPreferredSize().height);
331    availableScroll.setPreferredSize(d);
332    selectedScroll.setPreferredSize(d);
333  }
334
335  /**
336   * Enables the state of the components in the panel.
337   * @param enable whether to enable the components in the panel or not.
338   */
339  public void setEnabled(boolean enable)
340  {
341    super.setEnabled(enable);
342
343    selectedLabel.setEnabled(enable);
344    availableLabel.setEnabled(enable);
345    availableList.setEnabled(enable);
346    selectedList.setEnabled(enable);
347    availableScroll.setEnabled(enable);
348    selectedScroll.setEnabled(enable);
349
350    updateButtonEnabling();
351  }
352
353  /**
354   * Returns the available label contained in the panel.
355   * @return the available label contained in the panel.
356   */
357  public JLabel getAvailableLabel()
358  {
359    return availableLabel;
360  }
361
362  /**
363   * Returns the list of elements in the available list.
364   * @return the list of elements in the available list.
365   */
366  public SortableListModel<T> getAvailableListModel()
367  {
368    return availableListModel;
369  }
370
371  /**
372   * Returns the selected label contained in the panel.
373   * @return the selected label contained in the panel.
374   */
375  public JLabel getSelectedLabel()
376  {
377    return selectedLabel;
378  }
379
380  /**
381   * Returns the list of elements in the selected list.
382   * @return the list of elements in the selected list.
383   */
384  public SortableListModel<T> getSelectedListModel()
385  {
386    return selectedListModel;
387  }
388
389  private void updateButtonEnabling()
390  {
391    int index = availableList.getSelectedIndex();
392    add.setEnabled(index != -1 &&
393        index <availableListModel.getSize() && isEnabled());
394    index = selectedList.getSelectedIndex();
395    remove.setEnabled(index != -1 &&
396        index <selectedListModel.getSize() && isEnabled());
397
398    if (addAll != null)
399    {
400      addAll.setEnabled(availableListModel.getSize() > 0 && isEnabled());
401    }
402    if (removeAll != null)
403    {
404      removeAll.setEnabled(selectedListModel.getSize() > 0 && isEnabled());
405    }
406  }
407
408  /**
409   * Returns the available list.
410   * @return the available list.
411   */
412  public JList getAvailableList()
413  {
414    return availableList;
415  }
416
417  /**
418   * Returns the selected list.
419   * @return the selected list.
420   */
421  public JList getSelectedList()
422  {
423    return selectedList;
424  }
425
426  private void addClicked()
427  {
428    @SuppressWarnings("deprecation")
429    Object[] selectedObjects = availableList.getSelectedValues();
430    for (int i=0; i<selectedObjects.length; i++)
431    {
432      T value = AddRemovePanel.this.theClass.cast(selectedObjects[i]);
433      selectedListModel.add(value);
434      availableListModel.remove(value);
435    }
436    selectedListModel.fireContentsChanged(selectedListModel, 0,
437        selectedListModel.getSize());
438    availableListModel.fireContentsChanged(availableListModel, 0,
439        availableListModel.getSize());
440  }
441
442  private void removeClicked()
443  {
444    @SuppressWarnings("deprecation")
445    Object[] selectedObjects = selectedList.getSelectedValues();
446    for (int i=0; i<selectedObjects.length; i++)
447    {
448      T value = AddRemovePanel.this.theClass.cast(selectedObjects[i]);
449      availableListModel.add(value);
450      selectedListModel.remove(value);
451    }
452    selectedListModel.fireContentsChanged(selectedListModel, 0,
453        selectedListModel.getSize());
454    availableListModel.fireContentsChanged(availableListModel, 0,
455        availableListModel.getSize());
456  }
457}