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.plugin;
020
021import icy.file.FileUtil;
022import icy.gui.component.ImageComponent;
023import icy.gui.frame.IcyFrame;
024import icy.gui.util.ComponentUtil;
025import icy.gui.util.GuiUtil;
026import icy.network.NetworkUtil;
027import icy.plugin.PluginDescriptor;
028import icy.plugin.PluginLauncher;
029import icy.plugin.PluginLoader;
030import icy.system.IcyExceptionHandler;
031import icy.system.SystemUtil;
032import icy.system.thread.ThreadUtil;
033import icy.util.EventUtil;
034import icy.util.Random;
035import icy.util.StringUtil;
036
037import java.awt.BorderLayout;
038import java.awt.Color;
039import java.awt.Dimension;
040import java.awt.Font;
041import java.awt.Image;
042import java.awt.event.ActionEvent;
043import java.awt.event.ActionListener;
044import java.awt.event.MouseAdapter;
045import java.awt.event.MouseEvent;
046import java.io.IOException;
047
048import javax.swing.BorderFactory;
049import javax.swing.Box;
050import javax.swing.BoxLayout;
051import javax.swing.JButton;
052import javax.swing.JLabel;
053import javax.swing.JPanel;
054import javax.swing.JScrollPane;
055import javax.swing.JSeparator;
056import javax.swing.JTextPane;
057import javax.swing.SwingConstants;
058import javax.swing.event.HyperlinkEvent;
059import javax.swing.event.HyperlinkListener;
060
061/**
062 * @author stephane
063 */
064public class PluginDetailPanel extends IcyFrame implements HyperlinkListener
065{
066    private class ExecuteActionButton extends JButton implements ActionListener
067    {
068        /**
069         * 
070         */
071        private static final long serialVersionUID = 3096619820228575930L;
072
073        public ExecuteActionButton()
074        {
075            super("Execute");
076
077            setVisible(plugin.isActionable());
078
079            addActionListener(this);
080        }
081
082        @Override
083        public void actionPerformed(ActionEvent e)
084        {
085            PluginLauncher.start(plugin);
086        }
087    }
088
089    private class CloseActionButton extends JButton implements ActionListener
090    {
091        /**
092         * 
093         */
094        private static final long serialVersionUID = 4912851751410749786L;
095
096        public CloseActionButton()
097        {
098            super("Close");
099
100            addActionListener(this);
101        }
102
103        @Override
104        public void actionPerformed(ActionEvent e)
105        {
106            close();
107        }
108    }
109
110    final PluginDescriptor plugin;
111
112    // private final ChangesLogActionButton changesLogButton;
113    private final ExecuteActionButton executeButton;
114    private final CloseActionButton closeButton;
115
116    /**
117     * gui
118     */
119    final JPanel imagePanel;
120    final ImageComponent pluginImage;
121    final JLabel pluginPathLabel;
122    final JLabel pluginAuthorLabel;
123    final JLabel pluginWebsiteLabel;
124    final JLabel pluginEmailLabel;
125    final JTextPane pluginDescriptionText;
126    final JTextPane pluginChangeLogText;
127
128    /**
129     * @param plugin
130     */
131    public PluginDetailPanel(PluginDescriptor plugin)
132    {
133        super(plugin.getName() + " " + plugin.getVersion(), false, true);
134
135        this.plugin = plugin;
136
137        // changesLogButton = new ChangesLogActionButton();
138        executeButton = new ExecuteActionButton();
139        closeButton = new CloseActionButton();
140
141        setPreferredSize(new Dimension(640, 520));
142
143        // build top panel
144        pluginDescriptionText = new JTextPane();
145        pluginDescriptionText.setContentType("text/html");
146        pluginDescriptionText.setEditable(false);
147        pluginDescriptionText.setOpaque(false);
148        pluginDescriptionText.addHyperlinkListener(this);
149        ComponentUtil.setFixedHeight(pluginDescriptionText, 256);
150
151        final JScrollPane scDescription = new JScrollPane(pluginDescriptionText);
152        scDescription.setBorder(BorderFactory.createEmptyBorder(0, 4, 0, 4));
153
154        pluginImage = new ImageComponent(plugin.isImageLoaded() ? plugin.getImage() : PluginDescriptor.DEFAULT_IMAGE);
155
156        imagePanel = new JPanel(new BorderLayout());
157        imagePanel.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4));
158        imagePanel.add(pluginImage, BorderLayout.CENTER);
159
160        final JPanel topPanel = new JPanel(new BorderLayout());
161
162        topPanel.add(imagePanel, BorderLayout.WEST);
163        topPanel.add(scDescription, BorderLayout.CENTER);
164
165        // center panel
166        pluginPathLabel = new JLabel();
167        pluginPathLabel.addMouseListener(new MouseAdapter()
168        {
169            @Override
170            public void mouseClicked(MouseEvent e)
171            {
172                if (e.isConsumed())
173                    return;
174
175                if (EventUtil.isLeftMouseButton(e) && (e.getClickCount() == 2))
176                {
177                    final String path = pluginPathLabel.getText();
178
179                    try
180                    {
181                        if (!StringUtil.isEmpty(path) && !path.equals("---"))
182                            SystemUtil.openFolder(FileUtil.getDirectory(path));
183                    }
184                    catch (IOException e1)
185                    {
186                        IcyExceptionHandler.showErrorMessage(e1, false, false);
187                    }
188
189                    e.consume();
190                }
191            }
192        });
193        pluginAuthorLabel = new JLabel();
194        pluginWebsiteLabel = new JLabel();
195        pluginWebsiteLabel.addMouseListener(new MouseAdapter()
196        {
197            @Override
198            public void mouseClicked(MouseEvent e)
199            {
200                if (e.isConsumed())
201                    return;
202
203                if (EventUtil.isLeftMouseButton(e) && (e.getClickCount() == 2))
204                {
205                    final String url = pluginWebsiteLabel.getText();
206
207                    if (!StringUtil.isEmpty(url) && !url.equals("---"))
208                        NetworkUtil.openBrowser(url);
209
210                    e.consume();
211                }
212            }
213        });
214        pluginEmailLabel = new JLabel();
215
216        final JPanel infosPanel = new JPanel();
217        infosPanel.setBorder(BorderFactory.createEmptyBorder(4, 0, 0, 0));
218        infosPanel.setLayout(new BoxLayout(infosPanel, BoxLayout.PAGE_AXIS));
219
220        infosPanel.add(GuiUtil.createTabBoldLabel("Path", 1));
221        infosPanel.add(GuiUtil.createTabLabel(pluginPathLabel, 32));
222        infosPanel.add(Box.createVerticalStrut(4));
223        infosPanel.add(GuiUtil.createTabBoldLabel("Author", 1));
224        infosPanel.add(GuiUtil.createTabLabel(pluginAuthorLabel, 32));
225        infosPanel.add(Box.createVerticalStrut(4));
226        infosPanel.add(GuiUtil.createTabBoldLabel("Web site", 1));
227        infosPanel.add(GuiUtil.createTabLabel(pluginWebsiteLabel, 32));
228        infosPanel.add(Box.createVerticalStrut(4));
229        infosPanel.add(GuiUtil.createTabBoldLabel("Email", 1));
230        infosPanel.add(GuiUtil.createTabLabel(pluginEmailLabel, 32));
231        infosPanel.add(Box.createVerticalStrut(4));
232        infosPanel.add(Box.createVerticalGlue());
233
234        final JScrollPane scInfosPanel = new JScrollPane(infosPanel);
235        scInfosPanel.setPreferredSize(new Dimension(280, 128));
236        scInfosPanel.setMaximumSize(new Dimension(280, 2048));
237        scInfosPanel.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
238
239        pluginChangeLogText = new JTextPane();
240        pluginChangeLogText.setContentType("text/html");
241        pluginChangeLogText.setEditable(false);
242        pluginChangeLogText.setOpaque(false);
243        pluginChangeLogText.addHyperlinkListener(this);
244
245        final JScrollPane scChangeLog = new JScrollPane(pluginChangeLogText);
246        scChangeLog.setBorder(BorderFactory.createEmptyBorder(4, 0, 0, 0));
247
248        final JPanel changeLogPanel = new JPanel(new BorderLayout());
249        changeLogPanel.setBorder(BorderFactory.createEmptyBorder(0, 8, 4, 4));
250
251        changeLogPanel.add(GuiUtil.createTabBoldLabel("ChangeLog", 1), BorderLayout.NORTH);
252        changeLogPanel.add(GuiUtil.createLineBoxPanel(Box.createHorizontalStrut(32), scChangeLog), BorderLayout.CENTER);
253
254        final JPanel centerPanel = new JPanel(new BorderLayout());
255
256        centerPanel.add(new JSeparator(SwingConstants.HORIZONTAL), BorderLayout.NORTH);
257        centerPanel.add(scInfosPanel, BorderLayout.WEST);
258        centerPanel.add(changeLogPanel, BorderLayout.CENTER);
259
260        // bottom panel
261        final JPanel buttonsPanel = new JPanel();
262        buttonsPanel.setLayout(new BoxLayout(buttonsPanel, BoxLayout.LINE_AXIS));
263        buttonsPanel.setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8));
264
265        buttonsPanel.add(executeButton);
266        buttonsPanel.add(Box.createHorizontalGlue());
267        buttonsPanel.add(Box.createHorizontalStrut(4));
268        buttonsPanel.add(closeButton);
269
270        final JPanel bottomPanel = new JPanel(new BorderLayout());
271
272        bottomPanel.add(new JSeparator(SwingConstants.HORIZONTAL), BorderLayout.NORTH);
273        bottomPanel.add(buttonsPanel, BorderLayout.CENTER);
274
275        setLayout(new BorderLayout());
276
277        add(topPanel, BorderLayout.NORTH);
278        add(centerPanel, BorderLayout.CENTER);
279        add(bottomPanel, BorderLayout.SOUTH);
280
281        updateGui();
282
283        addToDesktopPane();
284        // random position for more fun
285        setLocation(10 * Random.nextInt(20) + 40, 10 * Random.nextInt(10) + 40);
286        setVisible(true);
287        requestFocus();
288
289        // some parts of the descriptor are not loaded --> async update
290        if (!plugin.isAllLoaded())
291        {
292            ThreadUtil.bgRun(new Runnable()
293            {
294                @Override
295                public void run()
296                {
297                    PluginDetailPanel.this.plugin.loadAll();
298
299                    // rebuild interface
300                    ThreadUtil.invokeLater(new Runnable()
301                    {
302                        @Override
303                        public void run()
304                        {
305                            updateGui();
306                        }
307                    });
308                }
309            });
310        }
311    }
312
313    void updateGui()
314    {
315        final Font sysFont = pluginAuthorLabel.getFont();
316        final Image img = plugin.getImage();
317        final String description = plugin.getDescription();
318        final String changesLog = plugin.getChangeLog();
319        final String author = plugin.getAuthor();
320        final String email = plugin.getEmail();
321        final String web = plugin.getWeb();
322
323        String jarPath = plugin.getPluginJarPath();
324
325        // can't get JAR path ?
326        if (StringUtil.isEmpty(jarPath))
327        {
328            // try to get the local plugin (we were probably using the online descriptor)
329            final PluginDescriptor localPlugin = PluginLoader.getPlugin(plugin.getClassName());
330
331            if (localPlugin != null)
332                jarPath = localPlugin.getPluginJarPath();
333        }
334
335        if (img == null)
336            pluginImage.setImage(PluginDescriptor.DEFAULT_IMAGE);
337        else
338            pluginImage.setImage(img);
339
340        if (StringUtil.isEmpty(description))
341            pluginDescriptionText.setText("No description");
342        else
343        {
344            if (StringUtil.containHtmlCR(description))
345                pluginDescriptionText.setText(StringUtil.removeCR(description));
346            else
347                pluginDescriptionText.setText(StringUtil.toHtmlCR(description));
348
349            pluginDescriptionText.setCaretPosition(0);
350        }
351
352        ComponentUtil.setJTextPaneFont(pluginDescriptionText, sysFont, Color.black);
353
354        if (StringUtil.isEmpty(changesLog))
355            pluginChangeLogText.setText("---");
356        else
357        {
358            pluginChangeLogText.setText(StringUtil.toHtmlCR(changesLog));
359            pluginChangeLogText.setCaretPosition(0);
360        }
361        ComponentUtil.setJTextPaneFont(pluginChangeLogText, new Font("courier", Font.PLAIN, 11), Color.black);
362        if (StringUtil.isEmpty(jarPath))
363        {
364            pluginPathLabel.setText("---");
365            pluginPathLabel.setToolTipText("");
366        }
367        else
368        {
369            pluginPathLabel.setText(jarPath);
370            pluginPathLabel.setToolTipText(jarPath + "  (double click to see file location)");
371        }
372        if (StringUtil.isEmpty(author))
373        {
374            pluginAuthorLabel.setText("---");
375            pluginAuthorLabel.setToolTipText("");
376        }
377        else
378        {
379            pluginAuthorLabel.setText(author);
380            pluginAuthorLabel.setToolTipText(author);
381        }
382        if (StringUtil.isEmpty(email))
383        {
384            pluginEmailLabel.setText("---");
385            pluginEmailLabel.setToolTipText("");
386        }
387        else
388        {
389            pluginEmailLabel.setText(email);
390            pluginEmailLabel.setToolTipText(email);
391        }
392        if (StringUtil.isEmpty(web))
393        {
394            pluginWebsiteLabel.setText("---");
395            pluginWebsiteLabel.setToolTipText("");
396        }
397        else
398        {
399            pluginWebsiteLabel.setText(web);
400            pluginWebsiteLabel.setToolTipText(web + "  (double click to open in browser)");
401        }
402
403        pack();
404    }
405
406    @Override
407    public void hyperlinkUpdate(HyperlinkEvent e)
408    {
409        if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED)
410            NetworkUtil.openBrowser(e.getURL());
411    }
412}