1/*
2 * ProGuard -- shrinking, optimization, obfuscation, and preverification
3 *             of Java bytecode.
4 *
5 * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu)
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the Free
9 * Software Foundation; either version 2 of the License, or (at your option)
10 * any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15 * more details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21package proguard.gui;
22
23import javax.swing.*;
24import javax.swing.event.*;
25import java.awt.*;
26import java.awt.event.*;
27import java.util.*;
28import java.util.List;
29
30/**
31 * This <code>Jpanel</code> allows the user to move and remove entries in a
32 * list and between lists. Extensions of this class should add buttons to add
33 * and possibly edit entries, and to set and get the resulting list.
34 *
35 * @author Eric Lafortune
36 */
37abstract class ListPanel extends JPanel
38{
39    protected final DefaultListModel listModel = new DefaultListModel();
40    protected final JList            list      = new JList(listModel);
41
42    protected int firstSelectionButton = 2;
43
44
45    protected ListPanel()
46    {
47        GridBagLayout layout = new GridBagLayout();
48        setLayout(layout);
49
50        GridBagConstraints listConstraints = new GridBagConstraints();
51        listConstraints.gridheight = GridBagConstraints.REMAINDER;
52        listConstraints.fill       = GridBagConstraints.BOTH;
53        listConstraints.weightx    = 1.0;
54        listConstraints.weighty    = 1.0;
55        listConstraints.anchor     = GridBagConstraints.NORTHWEST;
56        listConstraints.insets     = new Insets(0, 2, 0, 2);
57
58        // Make sure some buttons are disabled or enabled depending on whether
59        // the selection is empty or not.
60        list.addListSelectionListener(new ListSelectionListener()
61        {
62            public void valueChanged(ListSelectionEvent e)
63            {
64                enableSelectionButtons();
65            }
66        });
67
68        add(new JScrollPane(list), listConstraints);
69
70        // something like the following calls are up to the extending class:
71        //addAddButton();
72        //addEditButton();
73        //addRemoveButton();
74        //addUpButton();
75        //addDownButton();
76        //
77        //enableSelectionButtons();
78    }
79
80
81    protected void addRemoveButton()
82    {
83        JButton removeButton = new JButton(msg("remove"));
84        removeButton.addActionListener(new ActionListener()
85        {
86            public void actionPerformed(ActionEvent e)
87            {
88                // Remove the selected elements.
89                removeElementsAt(list.getSelectedIndices());
90            }
91        });
92
93        addButton(tip(removeButton, "removeTip"));
94    }
95
96
97    protected void addUpButton()
98    {
99        JButton upButton = new JButton(msg("moveUp"));
100        upButton.addActionListener(new ActionListener()
101        {
102            public void actionPerformed(ActionEvent e)
103            {
104                int[] selectedIndices = list.getSelectedIndices();
105                if (selectedIndices.length > 0 &&
106                    selectedIndices[0] > 0)
107                {
108                    // Move the selected elements up.
109                    moveElementsAt(selectedIndices, -1);
110                }
111            }
112        });
113
114        addButton(tip(upButton, "moveUpTip"));
115    }
116
117
118    protected void addDownButton()
119    {
120        JButton downButton = new JButton(msg("moveDown"));
121        downButton.addActionListener(new ActionListener()
122        {
123            public void actionPerformed(ActionEvent e)
124            {
125                int[] selectedIndices = list.getSelectedIndices();
126                if (selectedIndices.length > 0 &&
127                    selectedIndices[selectedIndices.length-1] < listModel.getSize()-1)
128                {
129                    // Move the selected elements down.
130                    moveElementsAt(selectedIndices, 1);
131                }
132            }
133        });
134
135        addButton(tip(downButton, "moveDownTip"));
136    }
137
138
139    /**
140     * Adds a button that allows to copy or move entries to another ListPanel.
141     *
142     * @param buttonTextKey the button text key.
143     * @param tipKey        the tool tip key.
144     * @param panel         the other ListPanel.
145     */
146    public void addCopyToPanelButton(String          buttonTextKey,
147                                     String          tipKey,
148                                     final ListPanel panel)
149    {
150        JButton moveButton = new JButton(msg(buttonTextKey));
151        moveButton.addActionListener(new ActionListener()
152        {
153            public void actionPerformed(ActionEvent e)
154            {
155                int[]    selectedIndices  = list.getSelectedIndices();
156                Object[] selectedElements = list.getSelectedValues();
157
158                // Remove the selected elements from this panel.
159                removeElementsAt(selectedIndices);
160
161                // Add the elements to the other panel.
162                panel.addElements(selectedElements);
163            }
164        });
165
166        addButton(tip(moveButton, tipKey));
167    }
168
169
170    protected void addButton(JComponent button)
171    {
172        GridBagConstraints buttonConstraints = new GridBagConstraints();
173        buttonConstraints.gridwidth = GridBagConstraints.REMAINDER;
174        buttonConstraints.fill      = GridBagConstraints.HORIZONTAL;
175        buttonConstraints.anchor    = GridBagConstraints.NORTHWEST;
176        buttonConstraints.insets    = new Insets(0, 2, 0, 2);
177
178        add(button, buttonConstraints);
179    }
180
181
182    /**
183     * Returns a list of all right-hand side buttons.
184     */
185    public List getButtons()
186    {
187        List list = new ArrayList(getComponentCount()-1);
188
189        // Add all buttons.
190        for (int index = 1; index < getComponentCount(); index++)
191        {
192            list.add(getComponent(index));
193        }
194
195        return list;
196    }
197
198
199    protected void addElement(Object element)
200    {
201        listModel.addElement(element);
202
203        // Make sure it is selected.
204        list.setSelectedIndex(listModel.size() - 1);
205    }
206
207
208    protected void addElements(Object[] elements)
209    {
210        // Add the elements one by one.
211        for (int index = 0; index < elements.length; index++)
212        {
213            listModel.addElement(elements[index]);
214        }
215
216        // Make sure they are selected.
217        int[] selectedIndices = new int[elements.length];
218        for (int index = 0; index < selectedIndices.length; index++)
219        {
220            selectedIndices[index] =
221                listModel.size() - selectedIndices.length + index;
222        }
223        list.setSelectedIndices(selectedIndices);
224    }
225
226
227    protected void moveElementsAt(int[] indices, int offset)
228    {
229        // Remember the selected elements.
230        Object[] selectedElements = list.getSelectedValues();
231
232        // Remove the selected elements.
233        removeElementsAt(indices);
234
235        // Update the element indices.
236        for (int index = 0; index < indices.length; index++)
237        {
238            indices[index] += offset;
239        }
240
241        // Reinsert the selected elements.
242        insertElementsAt(selectedElements, indices);
243    }
244
245
246    protected void insertElementsAt(Object[] elements, int[] indices)
247    {
248        for (int index = 0; index < elements.length; index++)
249        {
250            listModel.insertElementAt(elements[index], indices[index]);
251        }
252
253        // Make sure they are selected.
254        list.setSelectedIndices(indices);
255    }
256
257
258    protected void setElementAt(Object element, int index)
259    {
260        listModel.setElementAt(element, index);
261
262        // Make sure it is selected.
263        list.setSelectedIndex(index);
264    }
265
266
267    protected void setElementsAt(Object[] elements, int[] indices)
268    {
269        for (int index = 0; index < elements.length; index++)
270        {
271            listModel.setElementAt(elements[index], indices[index]);
272        }
273
274        // Make sure they are selected.
275        list.setSelectedIndices(indices);
276    }
277
278
279    protected void removeElementsAt(int[] indices)
280    {
281        for (int index = indices.length - 1; index >= 0; index--)
282        {
283            listModel.removeElementAt(indices[index]);
284        }
285
286        // Make sure nothing is selected.
287        list.clearSelection();
288
289        // Make sure the selection buttons are properly enabled,
290        // since the above method doesn't seem to notify the listener.
291        enableSelectionButtons();
292    }
293
294
295    protected void removeAllElements()
296    {
297        listModel.removeAllElements();
298
299        // Make sure the selection buttons are properly enabled,
300        // since the above method doesn't seem to notify the listener.
301        enableSelectionButtons();
302    }
303
304
305    /**
306     * Enables or disables the buttons that depend on a selection.
307     */
308    protected void enableSelectionButtons()
309    {
310        boolean selected = !list.isSelectionEmpty();
311
312        // Loop over all components, except the list itself and the Add button.
313        for (int index = firstSelectionButton; index < getComponentCount(); index++)
314        {
315            getComponent(index).setEnabled(selected);
316        }
317    }
318
319
320    /**
321     * Attaches the tool tip from the GUI resources that corresponds to the
322     * given key, to the given component.
323     */
324    private static JComponent tip(JComponent component, String messageKey)
325    {
326        component.setToolTipText(msg(messageKey));
327
328        return component;
329    }
330
331
332    /**
333     * Returns the message from the GUI resources that corresponds to the given
334     * key.
335     */
336    private static String msg(String messageKey)
337    {
338         return GUIResources.getMessage(messageKey);
339    }
340}
341