001/*
002 * Copyright 2010-2015 Institut Pasteur.
003 * 
004 * This file is part of Icy.
005 * 
006 * Icy is free software: you can redistribute it and/or modify
007 * it under the terms of the GNU General Public License as published by
008 * the Free Software Foundation, either version 3 of the License, or
009 * (at your option) any later version.
010 * 
011 * Icy is distributed in the hope that it will be useful,
012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
014 * GNU General Public License for more details.
015 * 
016 * You should have received a copy of the GNU General Public License
017 * along with Icy. If not, see <http://www.gnu.org/licenses/>.
018 */
019package icy.gui.inspector;
020
021import icy.gui.component.IcyTextField;
022import icy.gui.component.IcyTextField.TextChangeListener;
023import icy.main.Icy;
024import icy.resource.ResourceUtil;
025import icy.swimmingPool.SwimmingObject;
026import icy.swimmingPool.SwimmingPool;
027import icy.swimmingPool.SwimmingPoolEvent;
028import icy.swimmingPool.SwimmingPoolListener;
029import icy.system.thread.ThreadUtil;
030import icy.util.StringUtil;
031
032import java.awt.event.ActionEvent;
033import java.awt.event.ActionListener;
034import java.util.ArrayList;
035
036import javax.swing.BorderFactory;
037import javax.swing.Box;
038import javax.swing.BoxLayout;
039import javax.swing.DefaultComboBoxModel;
040import javax.swing.ImageIcon;
041import javax.swing.JButton;
042import javax.swing.JComboBox;
043import javax.swing.JLabel;
044import javax.swing.JPanel;
045import javax.swing.JScrollPane;
046import javax.swing.JTable;
047import javax.swing.ListSelectionModel;
048import javax.swing.event.ListSelectionEvent;
049import javax.swing.event.ListSelectionListener;
050import javax.swing.table.AbstractTableModel;
051import javax.swing.table.TableColumn;
052import javax.swing.table.TableColumnModel;
053
054/**
055 * @author Stephane
056 */
057public class SwimmingPoolPanel extends JPanel implements TextChangeListener, ListSelectionListener,
058        SwimmingPoolListener
059{
060    /**
061     * 
062     */
063    private static final long serialVersionUID = 1565643301342039659L;
064
065    static final String[] columnNames = {"", "Name", "Type"};
066
067    final SwimmingPool swimmingPool;
068    ArrayList<SwimmingObject> objects;
069
070    final AbstractTableModel tableModel;
071    final JTable table;
072
073    final JComboBox objectType;
074    final JPanel objectTypePanel;
075    final IcyTextField nameFilter;
076    final JButton refreshButton;
077    final JButton deleteAllButton;
078    final JButton deleteButton;
079
080    public SwimmingPoolPanel(boolean showTypeFilter, boolean showNameFilter, boolean showButtons)
081    {
082        super();
083
084        swimmingPool = Icy.getMainInterface().getSwimmingPool();
085        if (swimmingPool != null)
086            swimmingPool.addListener(this);
087
088        objects = new ArrayList<SwimmingObject>();
089
090        // GUI
091
092        objectType = new JComboBox(new DefaultComboBoxModel());
093        objectType.setToolTipText("Select type to display");
094        objectType.addActionListener(new ActionListener()
095        {
096            @Override
097            public void actionPerformed(ActionEvent e)
098            {
099                if (objectType.getSelectedIndex() != -1)
100                {
101                    refreshObjects();
102                    refreshTableData();
103                }
104            }
105        });
106
107        objectTypePanel = new JPanel();
108        objectTypePanel.setLayout(new BoxLayout(objectTypePanel, BoxLayout.PAGE_AXIS));
109        objectTypePanel.setVisible(showTypeFilter);
110
111        final JPanel internalRepPanel = new JPanel();
112        internalRepPanel.setLayout(new BoxLayout(internalRepPanel, BoxLayout.LINE_AXIS));
113
114        internalRepPanel.add(new JLabel("Object type :"));
115        internalRepPanel.add(Box.createHorizontalStrut(8));
116        internalRepPanel.add(objectType);
117        internalRepPanel.add(Box.createHorizontalGlue());
118
119        objectTypePanel.add(internalRepPanel);
120        objectTypePanel.add(Box.createVerticalStrut(8));
121
122        // need filter before load()
123        nameFilter = new IcyTextField();
124        nameFilter.addTextChangeListener(this);
125        nameFilter.setVisible(showNameFilter);
126
127        // build buttons panel
128
129        refreshButton = new JButton("Refresh");
130        refreshButton.addActionListener(new ActionListener()
131        {
132            @Override
133            public void actionPerformed(ActionEvent e)
134            {
135                // refresh list
136                refreshObjectTypeList();
137                refreshObjects();
138            }
139        });
140
141        deleteAllButton = new JButton("Delete all");
142        deleteAllButton.addActionListener(new ActionListener()
143        {
144            @Override
145            public void actionPerformed(ActionEvent e)
146            {
147                // delete all objects
148                swimmingPool.removeAll();
149            }
150        });
151
152        deleteButton = new JButton("Delete");
153        deleteButton.addActionListener(new ActionListener()
154        {
155            @Override
156            public void actionPerformed(ActionEvent e)
157            {
158                // delete selected objects
159                for (SwimmingObject so : getSelectedObjects())
160                    swimmingPool.remove(so);
161            }
162        });
163
164        final JPanel buttonsPanel = new JPanel();
165        buttonsPanel.setBorder(BorderFactory.createEmptyBorder(4, 8, 8, 8));
166        buttonsPanel.setLayout(new BoxLayout(buttonsPanel, BoxLayout.LINE_AXIS));
167        buttonsPanel.setVisible(showButtons);
168
169        buttonsPanel.add(refreshButton);
170        buttonsPanel.add(Box.createHorizontalStrut(64));
171        buttonsPanel.add(deleteAllButton);
172        buttonsPanel.add(Box.createHorizontalStrut(8));
173        buttonsPanel.add(deleteButton);
174        buttonsPanel.add(Box.createHorizontalGlue());
175
176        // build table
177        tableModel = new AbstractTableModel()
178        {
179            /**
180             * 
181             */
182            private static final long serialVersionUID = -8573364273165723214L;
183
184            @Override
185            public int getColumnCount()
186            {
187                return columnNames.length;
188            }
189
190            @Override
191            public String getColumnName(int column)
192            {
193                return columnNames[column];
194            }
195
196            @Override
197            public int getRowCount()
198            {
199                return objects.size();
200            }
201
202            @Override
203            public Object getValueAt(int row, int column)
204            {
205                final SwimmingObject so = objects.get(row);
206
207                switch (column)
208                {
209                    case 0:
210                        return ResourceUtil.scaleIcon(so.getIcon(), 24);
211
212                    case 1:
213                        return so.getName();
214
215                    case 2:
216                        return so.getObjectSimpleClassName();
217                }
218
219                return "";
220            }
221
222            @Override
223            public boolean isCellEditable(int row, int column)
224            {
225                return false;
226            }
227
228            @Override
229            public Class<?> getColumnClass(int columnIndex)
230            {
231                if (columnIndex == 0)
232                    return ImageIcon.class;
233
234                return String.class;
235            }
236        };
237
238        table = new JTable(tableModel);
239
240        final TableColumnModel colModel = table.getColumnModel();
241        TableColumn col;
242
243        // columns setting
244        col = colModel.getColumn(0);
245        col.setPreferredWidth(32);
246        col.setMinWidth(32);
247        col.setResizable(false);
248
249        col = colModel.getColumn(1);
250        col.setPreferredWidth(100);
251        col.setMinWidth(60);
252
253        col = colModel.getColumn(2);
254        col.setPreferredWidth(80);
255        col.setMinWidth(40);
256
257        table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
258        table.getSelectionModel().addListSelectionListener(this);
259        table.setRowHeight(24);
260        table.setColumnSelectionAllowed(false);
261        table.setRowSelectionAllowed(true);
262        table.setShowVerticalLines(false);
263        table.setAutoCreateRowSorter(true);
264        table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
265
266        setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));
267
268        add(Box.createVerticalStrut(2));
269        if (showTypeFilter)
270            add(objectTypePanel);
271        if (showNameFilter)
272            add(nameFilter);
273        if (showTypeFilter || showNameFilter)
274            add(Box.createVerticalStrut(8));
275        add(table.getTableHeader());
276        add(new JScrollPane(table));
277        if (showButtons)
278            add(buttonsPanel);
279
280        validate();
281
282        refreshObjectTypeList();
283        refreshObjects();
284    }
285
286    public void setTypeFilter(String type)
287    {
288        objectType.setSelectedItem(type);
289    }
290
291    public void setNameFilter(String name)
292    {
293        nameFilter.setText(name);
294    }
295
296    protected void refreshObjects()
297    {
298        if (swimmingPool != null)
299            objects = filterList(swimmingPool.getObjects(), nameFilter.getText());
300        else
301            objects.clear();
302    }
303
304    protected int getObjectIndex(SwimmingObject object)
305    {
306        return objects.indexOf(object);
307    }
308
309    protected int getObjectModelIndex(SwimmingObject object)
310    {
311        return getObjectIndex(object);
312    }
313
314    protected int getObjectTableIndex(SwimmingObject object)
315    {
316        final int ind = getObjectModelIndex(object);
317
318        if (ind == -1)
319            return ind;
320
321        try
322        {
323            return table.convertRowIndexToView(ind);
324        }
325        catch (IndexOutOfBoundsException e)
326        {
327            return -1;
328        }
329    }
330
331    public ArrayList<SwimmingObject> getSelectedObjects()
332    {
333        final ArrayList<SwimmingObject> result = new ArrayList<SwimmingObject>();
334
335        for (int rowIndex : table.getSelectedRows())
336        {
337            int index = -1;
338
339            if (rowIndex != -1)
340            {
341                try
342                {
343                    index = table.convertRowIndexToModel(rowIndex);
344                }
345                catch (IndexOutOfBoundsException e)
346                {
347                    // ignore
348                }
349            }
350
351            if ((index >= 0) || (index < objects.size()))
352                result.add(objects.get(index));
353        }
354
355        return result;
356    }
357
358    public void setSelectedObjects(ArrayList<SwimmingObject> sos)
359    {
360        table.clearSelection();
361
362        for (SwimmingObject so : sos)
363        {
364            final int index = getObjectTableIndex(so);
365
366            if (index > -1)
367                table.getSelectionModel().setSelectionInterval(index, index);
368        }
369    }
370
371    protected void refreshObjectTypeList()
372    {
373        final DefaultComboBoxModel model = (DefaultComboBoxModel) objectType.getModel();
374        final Object savedItem = model.getSelectedItem();
375
376        model.removeAllElements();
377        model.addElement("ALL");
378
379        if (swimmingPool != null)
380        {
381            for (String type : SwimmingObject.getObjectTypes(swimmingPool.getObjects()))
382                model.addElement(type);
383        }
384
385        if (savedItem != null)
386            model.setSelectedItem(savedItem);
387        else
388            objectType.setSelectedIndex(0);
389    }
390
391    private ArrayList<SwimmingObject> filterList(ArrayList<SwimmingObject> list, String nameFilterText)
392    {
393        final ArrayList<SwimmingObject> result = new ArrayList<SwimmingObject>();
394
395        final boolean typeEmpty = objectType.getSelectedIndex() == 0;
396        final boolean nameEmpty = StringUtil.isEmpty(nameFilterText, true);
397        final String typeFilter;
398        final String nameFilterUp;
399
400        if (!typeEmpty)
401            typeFilter = objectType.getSelectedItem().toString();
402        else
403            typeFilter = "";
404        if (!nameEmpty)
405            nameFilterUp = nameFilterText.toUpperCase();
406        else
407            nameFilterUp = "";
408
409        for (SwimmingObject so : list)
410        {
411            // search in name and type
412            if ((typeEmpty || so.getObjectSimpleClassName().equals(typeFilter))
413                    && (nameEmpty || (so.getName().indexOf(nameFilterUp) != -1)))
414                result.add(so);
415        }
416
417        return result;
418    }
419
420    protected void refreshTableData()
421    {
422        ThreadUtil.invokeLater(new Runnable()
423        {
424            @Override
425            public void run()
426            {
427                final ArrayList<SwimmingObject> sos = getSelectedObjects();
428
429                tableModel.fireTableDataChanged();
430
431                // restore previous selected objects if possible
432                setSelectedObjects(sos);
433            }
434        });
435    }
436
437    protected void refreshButtonsPanel()
438    {
439        deleteButton.setEnabled(getSelectedObjects().size() > 0);
440        deleteAllButton.setEnabled(objects.size() > 0);
441    }
442
443    protected void pluginsChanged()
444    {
445        refreshObjects();
446        refreshTableData();
447        refreshButtonsPanel();
448    }
449
450    @Override
451    public void textChanged(IcyTextField source, boolean validate)
452    {
453        pluginsChanged();
454    }
455
456    @Override
457    public void valueChanged(ListSelectionEvent e)
458    {
459        refreshButtonsPanel();
460
461        // TODO : send event to notify selection change
462    }
463
464    @Override
465    public void swimmingPoolChangeEvent(SwimmingPoolEvent swimmingPoolEvent)
466    {
467        refreshObjectTypeList();
468        refreshObjects();
469    }
470}