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.preferences;
020
021import icy.gui.component.IcyTable;
022import icy.gui.component.IcyTextField;
023import icy.gui.component.IcyTextField.TextChangeListener;
024import icy.gui.util.ComponentUtil;
025import icy.preferences.RepositoryPreferences;
026import icy.preferences.RepositoryPreferences.RepositoryInfo;
027import icy.system.thread.ThreadUtil;
028import icy.util.StringUtil;
029import icy.workspace.Workspace;
030
031import java.awt.BorderLayout;
032import java.awt.Dimension;
033import java.awt.event.ActionEvent;
034import java.awt.event.ActionListener;
035import java.util.ArrayList;
036import java.util.List;
037
038import javax.swing.BorderFactory;
039import javax.swing.Box;
040import javax.swing.BoxLayout;
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 abstract class WorkspaceListPreferencePanel extends PreferencePanel implements TextChangeListener,
058        ListSelectionListener
059{
060    /**
061     * 
062     */
063    private static final long serialVersionUID = -264986966623111155L;
064
065    private static final String BOTTOM_MESS = "*Workspace modification need application restart to take effect";
066
067    static final String[] columnNames = {"Name", "Description", "", "Enabled"};
068
069    // protected final Preferences preferences;
070    List<Workspace> workspaces;
071
072    final AbstractTableModel tableModel;
073    final JTable table;
074
075    final JComboBox repository;
076    final JPanel repositoryPanel;
077    final IcyTextField filter;
078    final JButton refreshButton;
079    final JButton action1Button;
080
081    private final Runnable buttonsStateUpdater;
082    private final Runnable tableDataRefresher;
083    private final Runnable workspaceListRefresher;
084    private final Runnable repositoriesUpdater;
085
086    final ActionListener repositoryActionListener;
087
088    WorkspaceListPreferencePanel(PreferenceFrame parent, String nodeName)
089    {
090        super(parent, nodeName, WorkspacePreferencePanel.NODE_NAME);
091
092        // preferences = Preferences.userRoot().node(preferencesId);
093        workspaces = new ArrayList<Workspace>();
094
095        buttonsStateUpdater = new Runnable()
096        {
097            @Override
098            public void run()
099            {
100                // need to be done on EDT
101                ThreadUtil.invokeNow(new Runnable()
102                {
103                    @Override
104                    public void run()
105                    {
106                        updateButtonsStateInternal();
107                    }
108                });
109            }
110        };
111        tableDataRefresher = new Runnable()
112        {
113            @Override
114            public void run()
115            {
116                // need to be done on EDT
117                ThreadUtil.invokeNow(new Runnable()
118                {
119                    @Override
120                    public void run()
121                    {
122                        refreshTableDataInternal();
123                    }
124                });
125            }
126        };
127        workspaceListRefresher = new Runnable()
128        {
129            @Override
130            public void run()
131            {
132                refreshWorkspacesInternal();
133            }
134        };
135        repositoriesUpdater = new Runnable()
136        {
137            @Override
138            public void run()
139            {
140                // need to be done on EDT
141                ThreadUtil.invokeNow(new Runnable()
142                {
143                    @Override
144                    public void run()
145                    {
146                        updateRepositoriesInternal();
147                    }
148                });
149            }
150        };
151
152        repositoryActionListener = new ActionListener()
153        {
154            @Override
155            public void actionPerformed(ActionEvent e)
156            {
157                repositoryChanged();
158            }
159        };
160
161        // build repository comboBox
162        repository = new JComboBox();
163        repository.setToolTipText("Select a repository");
164        repository.addActionListener(repositoryActionListener);
165
166        repositoryPanel = new JPanel();
167        repositoryPanel.setLayout(new BoxLayout(repositoryPanel, BoxLayout.PAGE_AXIS));
168        repositoryPanel.setVisible(false);
169
170        final JPanel internalRepPanel = new JPanel();
171        internalRepPanel.setLayout(new BoxLayout(internalRepPanel, BoxLayout.LINE_AXIS));
172
173        internalRepPanel.add(new JLabel("Repository :"));
174        internalRepPanel.add(Box.createHorizontalStrut(8));
175        internalRepPanel.add(repository);
176        internalRepPanel.add(Box.createHorizontalGlue());
177
178        repositoryPanel.add(internalRepPanel);
179        repositoryPanel.add(Box.createVerticalStrut(8));
180
181        // need filter before load()
182        filter = new IcyTextField();
183        filter.addTextChangeListener(this);
184
185        load();
186
187        // build buttons panel
188        final Dimension buttonsDim = new Dimension(100, 24);
189
190        refreshButton = new JButton("Reload list");
191        refreshButton.addActionListener(new ActionListener()
192        {
193            @Override
194            public void actionPerformed(ActionEvent e)
195            {
196                reloadWorkspaces();
197            }
198        });
199        ComponentUtil.setFixedSize(refreshButton, buttonsDim);
200
201        action1Button = new JButton("null");
202        action1Button.addActionListener(new ActionListener()
203        {
204            @Override
205            public void actionPerformed(ActionEvent e)
206            {
207                doAction1(getSelectedWorkspace());
208            }
209        });
210        action1Button.setVisible(false);
211        ComponentUtil.setFixedSize(action1Button, buttonsDim);
212
213        final JPanel buttonsPanel = new JPanel();
214        buttonsPanel.setBorder(BorderFactory.createEmptyBorder(4, 8, 8, 8));
215        buttonsPanel.setLayout(new BoxLayout(buttonsPanel, BoxLayout.PAGE_AXIS));
216
217        buttonsPanel.add(refreshButton);
218        buttonsPanel.add(Box.createVerticalStrut(64));
219        buttonsPanel.add(action1Button);
220        buttonsPanel.add(Box.createVerticalStrut(8));
221        buttonsPanel.add(Box.createVerticalGlue());
222
223        // build table
224        tableModel = new AbstractTableModel()
225        {
226            /**
227             * 
228             */
229            private static final long serialVersionUID = -8573364273165723214L;
230
231            @Override
232            public int getColumnCount()
233            {
234                return WorkspaceListPreferencePanel.this.getColumnCount();
235            }
236
237            @Override
238            public String getColumnName(int column)
239            {
240                return columnNames[column];
241            }
242
243            @Override
244            public int getRowCount()
245            {
246                return workspaces.size();
247            }
248
249            @Override
250            public Object getValueAt(int row, int column)
251            {
252                final Workspace workspace = workspaces.get(row);
253
254                switch (column)
255                {
256                    case 0:
257                        return workspace.getName();
258
259                    case 1:
260                        return workspace.getDescription();
261
262                    case 2:
263                        return getStateValue(workspace);
264
265                    case 3:
266                        return isWorkspaceEnable(workspace);
267                }
268
269                return "";
270            }
271
272            @Override
273            public boolean isCellEditable(int row, int column)
274            {
275                return (column == 3);
276            }
277
278            @Override
279            public Class<?> getColumnClass(int columnIndex)
280            {
281                if (columnIndex == 3)
282                    return Boolean.class;
283
284                return String.class;
285            }
286
287            @Override
288            public void setValueAt(Object aValue, int rowIndex, int columnIndex)
289            {
290                final Workspace workspace = workspaces.get(rowIndex);
291
292                if ((columnIndex == 3) && (aValue instanceof Boolean))
293                    setWorkspaceEnable(workspace, (Boolean) aValue);
294
295                // workspace setting changed, restart needed
296                getPreferenceFrame().setNeedRestart();
297            }
298        };
299
300        table = new IcyTable(tableModel);
301
302        final TableColumnModel colModel = table.getColumnModel();
303        TableColumn col;
304
305        // columns setting
306        col = colModel.getColumn(0);
307        col.setMinWidth(80);
308        col.setPreferredWidth(100);
309        col.setMaxWidth(120);
310
311        col = colModel.getColumn(1);
312        col.setMinWidth(160);
313        col.setPreferredWidth(240);
314        col.setMaxWidth(500);
315
316        col = colModel.getColumn(2);
317        col.setMinWidth(60);
318        col.setPreferredWidth(70);
319        col.setMaxWidth(80);
320
321        if (colModel.getColumnCount() > 3)
322        {
323            col = colModel.getColumn(3);
324            col.setMinWidth(60);
325            col.setPreferredWidth(60);
326            col.setMaxWidth(60);
327        }
328
329        table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
330        table.getSelectionModel().addListSelectionListener(this);
331        table.setColumnSelectionAllowed(false);
332        table.setRowSelectionAllowed(true);
333        table.setRowHeight(24);
334        table.setShowVerticalLines(false);
335        table.setAutoCreateRowSorter(true);
336        table.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
337
338        final JPanel tableTopPanel = new JPanel();
339
340        tableTopPanel.setLayout(new BoxLayout(tableTopPanel, BoxLayout.PAGE_AXIS));
341
342        tableTopPanel.add(Box.createVerticalStrut(2));
343        tableTopPanel.add(repositoryPanel);
344        tableTopPanel.add(filter);
345        tableTopPanel.add(Box.createVerticalStrut(8));
346        tableTopPanel.add(table.getTableHeader());
347
348        final JPanel tablePanel = new JPanel();
349
350        tablePanel.setLayout(new BorderLayout());
351
352        tablePanel.add(tableTopPanel, BorderLayout.NORTH);
353        tablePanel.add(new JScrollPane(table), BorderLayout.CENTER);
354
355        mainPanel.setLayout(new BorderLayout());
356
357        mainPanel.add(tablePanel, BorderLayout.CENTER);
358        mainPanel.add(buttonsPanel, BorderLayout.EAST);
359        mainPanel.add(new JLabel(BOTTOM_MESS), BorderLayout.SOUTH);
360
361        mainPanel.validate();
362    }
363
364    @Override
365    protected void closed()
366    {
367        super.closed();
368
369        // do not retains workspaces when frame is closed
370        workspaces.clear();
371    }
372
373    private List<Workspace> filterList(List<Workspace> list, String filter)
374    {
375        final List<Workspace> result = new ArrayList<Workspace>();
376        final boolean empty = StringUtil.isEmpty(filter, true);
377        final String filterUp;
378
379        if (!empty)
380            filterUp = filter.toUpperCase();
381        else
382            filterUp = "";
383
384        for (Workspace workspace : list)
385        {
386            final String name = workspace.getName().toUpperCase();
387            final String desc = workspace.getDescription().toUpperCase();
388
389            // search in name and description
390            if (empty || (name.indexOf(filterUp) != -1) || (desc.indexOf(filterUp) != -1))
391                result.add(workspace);
392        }
393
394        return result;
395    }
396
397    protected abstract void doAction1(Workspace workspace);
398
399    protected abstract void repositoryChanged();
400
401    protected abstract void reloadWorkspaces();
402
403    protected abstract String getStateValue(Workspace workspace);
404
405    protected abstract int getColumnCount();
406
407    protected abstract List<Workspace> getWorkspaces();
408
409    protected abstract void updateButtonsStateInternal();
410
411    protected final void updateButtonsState()
412    {
413        ThreadUtil.runSingle(buttonsStateUpdater);
414    }
415
416    protected void updateRepositoriesInternal()
417    {
418        // final RepositoryPreferencePanel panel = (RepositoryPreferencePanel)
419        // getPreferencePanel(RepositoryPreferencePanel.class);
420        // // refresh repositories list (use list from GUI)
421        // final ArrayList<RepositoryInfo> repositeries = panel.repositories;
422
423        // refresh repositories list
424        final ArrayList<RepositoryInfo> repositeries = RepositoryPreferences.getRepositeries();
425
426        final RepositoryInfo savedRepository = (RepositoryInfo) repository.getSelectedItem();
427
428        // needed to disable events during update time
429        repository.removeActionListener(repositoryActionListener);
430
431        repository.removeAllItems();
432        for (RepositoryInfo repos : repositeries)
433            if (repos.isEnabled())
434                repository.addItem(repos);
435        repository.setEnabled(true);
436
437        repository.addActionListener(repositoryActionListener);
438
439        boolean selected = false;
440
441        // try to set back the old selected repository
442        if (savedRepository != null)
443        {
444            final String repositoryName = savedRepository.getName();
445
446            for (int ind = 0; ind < repository.getItemCount(); ind++)
447            {
448                final RepositoryInfo repo = (RepositoryInfo) repository.getItemAt(ind);
449
450                if ((repo != null) && (repo.getName().equals(repositoryName)))
451                {
452                    repository.setSelectedIndex(ind);
453                    selected = true;
454                    break;
455                }
456            }
457        }
458
459        // manually launch the action
460        if (!selected)
461            repository.setSelectedIndex((repository.getItemCount() > 0) ? 0 : -1);
462
463        // avoid automatic minimum size here
464        repository.setMinimumSize(new Dimension(48, 18));
465    }
466
467    protected final void updateRepositories()
468    {
469        ThreadUtil.runSingle(repositoriesUpdater);
470    }
471
472    protected Boolean isWorkspaceEnable(Workspace workspace)
473    {
474        return Boolean.FALSE;
475    }
476
477    protected void setWorkspaceEnable(Workspace workspace, Boolean value)
478    {
479        // ignore
480    }
481
482    protected void refreshWorkspacesInternal()
483    {
484        workspaces = filterList(getWorkspaces(), filter.getText());
485        // refresh table data
486        refreshTableData();
487    }
488
489    protected final void refreshWorkspaces()
490    {
491        ThreadUtil.runSingle(workspaceListRefresher);
492    }
493
494    protected int getWorkspaceIndex(Workspace workspace)
495    {
496        return workspaces.indexOf(workspace);
497    }
498
499    protected int getWorkspaceModelIndex(Workspace workspace)
500    {
501        return getWorkspaceIndex(workspace);
502    }
503
504    protected int getWorkspaceTableIndex(Workspace workspace)
505    {
506        final int ind = getWorkspaceModelIndex(workspace);
507
508        if (ind == -1)
509            return ind;
510
511        try
512        {
513
514            return table.convertRowIndexToView(ind);
515        }
516        catch (IndexOutOfBoundsException e)
517        {
518            return -1;
519        }
520    }
521
522    Workspace getSelectedWorkspace()
523    {
524        int index;
525
526        index = table.getSelectedRow();
527        if (index == -1)
528            return null;
529
530        try
531        {
532            index = table.convertRowIndexToModel(index);
533        }
534        catch (IndexOutOfBoundsException e)
535        {
536            index = -1;
537        }
538
539        if ((index < 0) || (index >= workspaces.size()))
540            return null;
541
542        return workspaces.get(index);
543    }
544
545    void setSelectedWorkspace(Workspace workspace)
546    {
547        final int index = getWorkspaceTableIndex(workspace);
548
549        if (index > -1)
550        {
551            table.clearSelection();
552            table.getSelectionModel().setSelectionInterval(index, index);
553        }
554    }
555
556    @Override
557    protected void load()
558    {
559
560    }
561
562    @Override
563    protected void save()
564    {
565        // reload repositories as some parameter as beta flag can have changed
566        updateRepositories();
567    }
568
569    protected void refreshTableDataInternal()
570    {
571        final Workspace workspace = getSelectedWorkspace();
572
573        tableModel.fireTableDataChanged();
574
575        // restore previous selected workspace if possible
576        setSelectedWorkspace(workspace);
577
578        // update buttons state
579        updateButtonsState();
580    }
581
582    protected final void refreshTableData()
583    {
584        ThreadUtil.runSingle(tableDataRefresher);
585    }
586
587    protected void workspacesChanged()
588    {
589        refreshWorkspaces();
590    }
591
592    @Override
593    public void textChanged(IcyTextField source, boolean validate)
594    {
595        workspacesChanged();
596    }
597
598    @Override
599    public void valueChanged(ListSelectionEvent e)
600    {
601        updateButtonsState();
602    }
603}