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