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.dialog.ConfirmDialog;
022import icy.plugin.PluginDescriptor;
023import icy.plugin.PluginInstaller;
024import icy.plugin.PluginInstaller.PluginInstallerListener;
025import icy.plugin.PluginLoader;
026import icy.plugin.PluginLoader.PluginLoaderEvent;
027import icy.plugin.PluginLoader.PluginLoaderListener;
028import icy.plugin.PluginRepositoryLoader;
029import icy.plugin.PluginRepositoryLoader.PluginRepositoryLoaderListener;
030import icy.plugin.PluginUpdater;
031
032import java.util.ArrayList;
033import java.util.List;
034
035/**
036 * @author Stephane
037 */
038public class PluginLocalPreferencePanel extends PluginListPreferencePanel implements PluginLoaderListener,
039        PluginInstallerListener, PluginRepositoryLoaderListener
040{
041    private enum PluginLocalState
042    {
043        NULL, UPDATING, REMOVING, HAS_UPDATE, NO_UPDATE, CHECKING_UPDATE
044    }
045
046    /**
047     * 
048     */
049    private static final long serialVersionUID = -6732331255351202922L;
050
051    public static final String NODE_NAME = "Local Plugin";
052
053    PluginLocalPreferencePanel(PreferenceFrame parent)
054    {
055        super(parent, NODE_NAME, PluginPreferencePanel.NODE_NAME);
056
057        PluginRepositoryLoader.addListener(this);
058        PluginLoader.addListener(this);
059        PluginInstaller.addListener(this);
060
061        // remove last column not used here
062        table.removeColumn(table.getColumn(columnIds[4]));
063
064        action1Button.setText("Delete");
065        action1Button.setVisible(true);
066        action2Button.setText("Check update");
067        action2Button.setVisible(true);
068
069        pluginsChanged();
070    }
071
072    @Override
073    protected void closed()
074    {
075        super.closed();
076
077        PluginRepositoryLoader.removeListener(this);
078        PluginInstaller.removeListener(this);
079        PluginLoader.removeListener(this);
080    }
081
082    private PluginLocalState getPluginLocalState(PluginDescriptor plugin)
083    {
084        if (plugin != null)
085        {
086            if (!PluginRepositoryLoader.isLoaded())
087                return PluginLocalState.CHECKING_UPDATE;
088
089            if ((PluginInstaller.isDesinstallingPlugin(plugin)))
090                return PluginLocalState.REMOVING;
091
092            // get online version
093            final PluginDescriptor onlinePlugin = PluginUpdater.getUpdate(plugin);
094
095            // update available ?
096            if (onlinePlugin != null)
097            {
098                if (PluginInstaller.isInstallingPlugin(onlinePlugin))
099                    return PluginLocalState.UPDATING;
100
101                return PluginLocalState.HAS_UPDATE;
102            }
103
104            if (plugin.isInstalled())
105                return PluginLocalState.NO_UPDATE;
106
107            // here the plugin has just been removed but plugin list is not yet updated
108            return PluginLocalState.REMOVING;
109        }
110
111        return PluginLocalState.NULL;
112    }
113
114    @Override
115    protected void doAction1()
116    {
117        final List<PluginDescriptor> selectedPlugins = getSelectedPlugins();
118        final List<PluginDescriptor> toRemove = new ArrayList<PluginDescriptor>();
119
120        for (PluginDescriptor plugin : selectedPlugins)
121            if (!PluginInstaller.isDesinstallingPlugin(plugin) && plugin.isInstalled())
122                toRemove.add(plugin);
123
124        // nothing to remove
125        if (toRemove.isEmpty())
126            return;
127
128        // get dependants plugins
129        final List<PluginDescriptor> dependants = PluginInstaller.getLocalDependenciesFrom(toRemove);
130        // delete the one we plan to remove
131        dependants.removeAll(toRemove);
132
133        String message = "<html>";
134
135        if (!dependants.isEmpty())
136        {
137            message = message + "The following plugin(s) won't work anymore :<br>";
138
139            for (PluginDescriptor depPlug : dependants)
140                message = message + depPlug.getName() + " " + depPlug.getVersion() + "<br>";
141
142            message = message + "<br>";
143        }
144
145        message = message + "Are you sure you want to remove selected plugin(s) ?</html>";
146
147        if (ConfirmDialog.confirm(message))
148        {
149            // remove plugins
150            for (PluginDescriptor plugin : toRemove)
151                PluginInstaller.desinstall(plugin, false, true);
152        }
153
154        // refresh state
155        refreshTableData();
156    }
157
158    @Override
159    protected void doAction2()
160    {
161        final List<PluginDescriptor> selectedPlugins = getSelectedPlugins();
162
163        for (PluginDescriptor plugin : selectedPlugins)
164        {
165            final PluginDescriptor onlinePlugin = PluginUpdater.getUpdate(plugin);
166            // install update
167            if (onlinePlugin != null)
168                PluginInstaller.install(onlinePlugin, true);
169        }
170
171        // refresh state
172        refreshTableData();
173    }
174
175    @Override
176    protected void repositoryChanged()
177    {
178        // do nothing here
179    }
180
181    @Override
182    protected void reloadPlugins()
183    {
184        PluginLoader.reloadAsynch();
185        // so we display the empty list during reload
186        pluginsChanged();
187    }
188
189    @Override
190    protected String getStateValue(PluginDescriptor plugin)
191    {
192        if (plugin == null)
193            return "";
194
195        switch (getPluginLocalState(plugin))
196        {
197            case REMOVING:
198                return "removing...";
199
200            case CHECKING_UPDATE:
201                return "checking...";
202
203            case UPDATING:
204                return "updating...";
205
206            case HAS_UPDATE:
207                return "update available";
208
209            case NO_UPDATE:
210                return "";
211        }
212
213        return "";
214    }
215
216    @Override
217    protected List<PluginDescriptor> getPlugins()
218    {
219        // loading...
220        if (PluginLoader.isLoading())
221            return new ArrayList<PluginDescriptor>();
222
223        final List<PluginDescriptor> result = PluginLoader.getPlugins(false);
224
225        // only display installed plugins (this hide inner or dev plugins)
226        for (int i = result.size() - 1; i >= 0; i--)
227            if (!result.get(i).isInstalled())
228                result.remove(i);
229
230        return result;
231    }
232
233    @Override
234    protected void updateButtonsStateInternal()
235    {
236        super.updateButtonsStateInternal();
237
238        final List<PluginDescriptor> selectedPlugins = getSelectedPlugins();
239        final boolean selected = (selectedPlugins.size() > 0);
240
241        if (PluginLoader.isLoading())
242        {
243            refreshButton.setText("Reloading...");
244            refreshButton.setEnabled(false);
245        }
246        else
247        {
248            refreshButton.setText("Reload list");
249            refreshButton.setEnabled(true);
250        }
251
252        if (!selected)
253        {
254            action1Button.setEnabled(false);
255            action2Button.setEnabled(false);
256            return;
257        }
258
259        boolean removing = true;
260        for (PluginDescriptor plugin : selectedPlugins)
261        {
262            if (!PluginInstaller.isDesinstallingPlugin(plugin))
263            {
264                removing = false;
265                break;
266            }
267        }
268
269        // special case where plugins are currently begin removed
270        if (removing)
271        {
272            action1Button.setText("Removing...");
273            action1Button.setEnabled(false);
274        }
275        else
276        {
277            action1Button.setText("Remove");
278            action1Button.setEnabled(true);
279        }
280
281        PluginLocalState state = PluginLocalState.NULL;
282
283        for (PluginDescriptor plugin : selectedPlugins)
284        {
285            switch (getPluginLocalState(plugin))
286            {
287                case CHECKING_UPDATE:
288                    if ((state == PluginLocalState.NULL) || (state == PluginLocalState.NO_UPDATE))
289                        state = PluginLocalState.CHECKING_UPDATE;
290                    break;
291
292                case UPDATING:
293                    if ((state == PluginLocalState.NULL) || (state == PluginLocalState.NO_UPDATE)
294                            || (state == PluginLocalState.CHECKING_UPDATE))
295                        state = PluginLocalState.UPDATING;
296                    break;
297
298                case HAS_UPDATE:
299                    state = PluginLocalState.HAS_UPDATE;
300                    break;
301
302                case NO_UPDATE:
303                    if (state == PluginLocalState.NULL)
304                        state = PluginLocalState.NO_UPDATE;
305                    break;
306            }
307        }
308
309        switch (state)
310        {
311            case CHECKING_UPDATE:
312                action1Button.setEnabled(false);
313                action2Button.setText("Checking...");
314                action2Button.setEnabled(false);
315                break;
316
317            case UPDATING:
318                action1Button.setEnabled(false);
319                action2Button.setText("Updating...");
320                action2Button.setEnabled(false);
321                break;
322
323            case HAS_UPDATE:
324                action2Button.setText("Update");
325                action2Button.setEnabled(true);
326                break;
327
328            case NO_UPDATE:
329                action2Button.setText("No update");
330                action2Button.setEnabled(false);
331                break;
332
333            case NULL:
334                action1Button.setEnabled(false);
335                action2Button.setEnabled(false);
336                break;
337        }
338
339        // keep delete button enabled only if we can actually delete the plugin
340        if (action1Button.isEnabled())
341        {
342            boolean canDelete = false;
343            for (PluginDescriptor plugin : selectedPlugins)
344            {
345                if (plugin.isInstalled())
346                {
347                    canDelete = true;
348                    break;
349                }
350            }
351
352            action1Button.setEnabled(canDelete);
353        }
354    }
355
356    @Override
357    public void pluginLoaderChanged(PluginLoaderEvent e)
358    {
359        pluginsChanged();
360    }
361
362    @Override
363    public void pluginInstalled(PluginDescriptor plugin, boolean success)
364    {
365        updateButtonsState();
366    }
367
368    @Override
369    public void pluginRemoved(PluginDescriptor plugin, boolean success)
370    {
371        updateButtonsState();
372    }
373
374    @Override
375    public void pluginRepositeryLoaderChanged(PluginDescriptor plugin)
376    {
377        refreshTableData();
378    }
379}