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