/* * ManagePanel.java - Manages plugins * :tabSize=4:indentSize=4:noTabs=false: * :folding=explicit:collapseFolds=1: * * Copyright (C) 2002 Kris Kopicki * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package org.gjt.sp.jedit.pluginmgr; //{{{ Imports import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.FocusAdapter; import java.awt.event.FocusEvent; import java.awt.event.InputEvent; import java.awt.event.KeyEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.net.URL; import java.util.*; import java.util.List; import javax.swing.*; import javax.swing.border.EmptyBorder; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import javax.swing.event.TableModelEvent; import javax.swing.table.AbstractTableModel; import javax.swing.table.DefaultTableCellRenderer; import javax.swing.table.JTableHeader; import javax.swing.table.TableColumn; import org.gjt.sp.jedit.*; import org.gjt.sp.jedit.io.FileVFS; import org.gjt.sp.jedit.io.VFS; import org.gjt.sp.jedit.io.VFSManager; import org.gjt.sp.jedit.msg.PropertiesChanged; import java.util.concurrent.ConcurrentHashMap; import java.io.*; import org.gjt.sp.jedit.browser.VFSBrowser; import org.gjt.sp.jedit.browser.VFSFileChooserDialog; import org.gjt.sp.jedit.gui.RolloverButton; import org.gjt.sp.jedit.help.*; import org.gjt.sp.util.Log; import org.gjt.sp.util.IOUtilities; import org.gjt.sp.util.XMLUtilities; import org.gjt.sp.util.StandardUtilities; import org.xml.sax.Attributes; import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler; //}}} /** * The ManagePanel is the JPanel that shows the installed plugins. */ public class ManagePanel extends JPanel { //{{{ Private members private final JCheckBox hideLibraries; private final JTable table; private final JScrollPane scrollpane; private final PluginDetailPanel pluginDetailPanel; private final PluginTableModel pluginModel; private final PluginManager window; private JPopupMenu popup; private Set<String> selectedPlugins; private Set<String> jarNames; //}}} //{{{ ManagePanel constructor public ManagePanel(PluginManager window) { super(new BorderLayout(12,12)); this.window = window; setBorder(new EmptyBorder(12,12,12,12)); Box topBox = new Box(BoxLayout.X_AXIS); topBox.add(hideLibraries = new HideLibrariesButton()); add(BorderLayout.NORTH,topBox); /* Create the plugin table */ table = new JTable(pluginModel = new PluginTableModel()); table.setShowGrid(false); table.setIntercellSpacing(new Dimension(0,0)); table.setRowHeight(table.getRowHeight() + 2); table.setPreferredScrollableViewportSize(new Dimension(500,300)); table.setDefaultRenderer(Object.class, new TextRenderer( (DefaultTableCellRenderer)table.getDefaultRenderer(Object.class))); table.addFocusListener(new TableFocusHandler()); table.getSelectionModel().addListSelectionListener(new TableSelectionListener()); InputMap tableInputMap = table.getInputMap(JComponent.WHEN_FOCUSED); ActionMap tableActionMap = table.getActionMap(); tableInputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_TAB,0),"tabOutForward"); tableActionMap.put("tabOutForward",new KeyboardAction(KeyboardCommand.TAB_OUT_FORWARD)); tableInputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_TAB,InputEvent.SHIFT_MASK),"tabOutBack"); tableActionMap.put("tabOutBack",new KeyboardAction(KeyboardCommand.TAB_OUT_BACK)); tableInputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE,0),"editPlugin"); tableActionMap.put("editPlugin",new KeyboardAction(KeyboardCommand.EDIT_PLUGIN)); tableInputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER,0),"closePluginManager"); tableActionMap.put("closePluginManager",new KeyboardAction(KeyboardCommand.CLOSE_PLUGIN_MANAGER)); TableColumn col1 = table.getColumnModel().getColumn(0); TableColumn col2 = table.getColumnModel().getColumn(1); TableColumn col3 = table.getColumnModel().getColumn(2); TableColumn col4 = table.getColumnModel().getColumn(3); col1.setPreferredWidth(30); col1.setMinWidth(30); col1.setMaxWidth(30); col1.setResizable(false); col2.setPreferredWidth(300); col3.setPreferredWidth(100); col4.setPreferredWidth(100); JTableHeader header = table.getTableHeader(); header.setReorderingAllowed(false); HeaderMouseHandler mouseHandler = new HeaderMouseHandler(); header.addMouseListener(mouseHandler); table.addMouseListener(mouseHandler); scrollpane = new JScrollPane(table); scrollpane.getViewport().setBackground(table.getBackground()); pluginDetailPanel = new PluginDetailPanel(); scrollpane.setPreferredSize(new Dimension(400,400)); JSplitPane split = new JSplitPane(JSplitPane.VERTICAL_SPLIT, true, scrollpane, pluginDetailPanel); add(BorderLayout.CENTER, split); split.setResizeWeight(0.75); /* Create button panel */ Box buttons = new Box(BoxLayout.X_AXIS); buttons.add(new RemoveButton()); buttons.add(new SaveButton()); buttons.add(new RestoreButton()); buttons.add(new FindOrphan()); buttons.add(Box.createGlue()); buttons.add(new HelpButton()); add(BorderLayout.SOUTH,buttons); pluginModel.update(); } //}}} //{{{ update() method public void update() { pluginModel.update(); } //}}} // {{{ class ManagePanelRestoreHandler /** * For handling the XML parse events of a plugin set. * Selects the same plugins that are in that set. * @since jEdit 4.3pre10 */ private class ManagePanelRestoreHandler extends DefaultHandler { ManagePanelRestoreHandler() { selectedPlugins = new HashSet<String>(); jarNames = new HashSet<String>(); } @Override public void startElement(String uri, String localName, String qName, Attributes attrs) throws SAXException { if (localName.equals("plugin")) { String jarName = attrs.getValue("jar"); String name = attrs.getValue("name"); selectedPlugins.add(name); jarNames.add(jarName); } } }//}}} //{{{ loadPluginSet() method boolean loadPluginSet(String path) { VFS vfs = VFSManager.getVFSForPath(path); Object session = vfs.createVFSSession(path, this); try { InputStream is = vfs._createInputStream(session, path, false, this); XMLUtilities.parseXML(is, new ManagePanelRestoreHandler()); is.close(); int rowCount = pluginModel.getRowCount(); for (int i=0 ; i<rowCount ; i++) { Entry ent = pluginModel.getEntry(i); String name = ent.name; if (name != null) { pluginModel.setValueAt(selectedPlugins.contains(name), i, 0); } else { String jarPath = ent.jar; String jarName = jarPath.substring(1 + jarPath.lastIndexOf(File.separatorChar)); try { pluginModel.setValueAt(jarNames.contains(jarName), i, 0); } catch (Exception e) { Log.log(Log.WARNING, this, "Exception thrown loading: " + jarName, e); } } } } catch (Exception e) { Log.log(Log.ERROR, this, "Loading Pluginset Error", e); return false; } pluginModel.update(); return true; }//}}} //{{{ getDeclaredJars() method /** * Returns a collection of declared jars in the plugin. * If the plugin is loaded use {@link org.gjt.sp.jedit.PluginJAR#getRequiredJars()} * instead * * @param jarName the jar name of the plugin * @return a collection containing jars path * @throws IOException if jEdit cannot generate cache * @since jEdit 4.3pre12 */ private static Collection<String> getDeclaredJars(String jarName) throws IOException { Collection<String> jarList = new ArrayList<String>(); PluginJAR pluginJAR = new PluginJAR(new File(jarName)); PluginJAR.PluginCacheEntry pluginCacheEntry = PluginJAR.getPluginCache(pluginJAR); if (pluginCacheEntry == null) { try { pluginCacheEntry = pluginJAR.generateCache(); } finally { IOUtilities.closeQuietly(pluginJAR.getZipFile()); } } if(pluginCacheEntry == null) { // this happens when, for some reason, two versions // of a plugin are installed, e.g when XSLT.jar and // xslt.jar are both in $JEDIT_HOME/jars on Linux. Log.log(Log.WARNING, ManagePanel.class, "couldn't load plugin "+pluginJAR.getPath() +" (most likely other version exists)"); } else { Properties cachedProperties = pluginCacheEntry.cachedProperties; String jars = cachedProperties.getProperty("plugin." + pluginCacheEntry.pluginClass + ".jars"); if (jars != null) { String dir = MiscUtilities.getParentOfPath(pluginJAR.getPath()); StringTokenizer st = new StringTokenizer(jars); while (st.hasMoreTokens()) { String _jarPath = MiscUtilities.constructPath(dir, st.nextToken()); if (new File(_jarPath).exists()) jarList.add(_jarPath); } } } jarList.add(jarName); return jarList; }//}}} //{{{ Inner classes //{{{ Entry class class Entry { static final String LOADED = "loaded"; static final String NOT_LOADED = "not-loaded"; /** Partially loaded, and marked as "error" due to unsatisfied depends. */ static final String ERROR = "error"; /** Not loaded, marked Unsupported in plugin manager. */ static final String DISABLED = "disabled"; final String status; /** The jar path. */ final String jar; String clazz, name, version, author, docs; /** The description property of the plugin. */ String description; /** The dependencies of the plugin. */ Set<String> depends; EditPlugin plugin; /** * The jars referenced in the props file of the plugin. * plugin.clazz.jars property and * plugin.clazz.files property */ final List<String> jars; /** The data size. */ String dataSize; /** * Constructor used for jars that aren't loaded. * * @param jar jar file name */ Entry(String jar) { jars = new LinkedList<String>(); this.jar = jar; jars.add(this.jar); if (jEdit.getBooleanProperty("plugin." + MiscUtilities.getFileName(jar) + ".disabled")) status = DISABLED; else status = NOT_LOADED; } /** * Constructor used for loaded jars. * * @param jar the pluginJar */ Entry(PluginJAR jar) { jars = new LinkedList<String>(); this.jar = jar.getPath(); jars.add(this.jar); plugin = jar.getPlugin(); if(plugin != null) { status = plugin instanceof EditPlugin.Broken ? ERROR : LOADED; clazz = plugin.getClassName(); name = jEdit.getProperty("plugin."+clazz+".name"); version = jEdit.getProperty("plugin."+clazz+".version"); author = jEdit.getProperty("plugin."+clazz+".author"); docs = jEdit.getProperty("plugin."+clazz+".docs"); description = jEdit.getProperty("plugin."+clazz+".description"); String jarsProp = jEdit.getProperty("plugin."+clazz+".jars"); if(jarsProp != null) { String directory = MiscUtilities.getParentOfPath(this.jar); StringTokenizer st = new StringTokenizer(jarsProp); while(st.hasMoreElements()) { jars.add(MiscUtilities.constructPath( directory,st.nextToken())); } } String filesProp = jEdit.getProperty("plugin."+clazz+".files"); if(filesProp != null) { String directory = MiscUtilities.getParentOfPath(this.jar); StringTokenizer st = new StringTokenizer(filesProp); while(st.hasMoreElements()) { jars.add(MiscUtilities.constructPath( directory,st.nextToken())); } } } else { status = LOADED; } } /** * @return A list of the names of the dependencies, e.g. ErrorList or ProjectViewer. */ public Set<String> getDependencies() { if (plugin == null) return null; Set<String> depends = null; PluginJAR jar = plugin.getPluginJAR(); String cn = plugin.getClassName(); Set<String> requiredJars = jar.getDependencies(cn); if (requiredJars != null && !requiredJars.isEmpty()) { depends = new HashSet<String>(); for (String dep : requiredJars) { Entry e = pluginModel.getEntry(dep); if (e != null) depends.add(e.name); } } return depends; } } //}}} //{{{ PluginTableModel class private class PluginTableModel extends AbstractTableModel { private final List<Entry> entries; private int sortType = EntryCompare.NAME; private ConcurrentHashMap<String, Object> unloaded; // private HashSet<String> unloaded; //{{{ Constructor PluginTableModel() { entries = new ArrayList<Entry>(); } //}}} //{{{ getColumnCount() method @Override public int getColumnCount() { return 5; } //}}} //{{{ getColumnClass() method @Override public Class getColumnClass(int columnIndex) { switch (columnIndex) { case 0: return Boolean.class; default: return Object.class; } } //}}} //{{{ getColumnName() method @Override public String getColumnName(int column) { switch (column) { case 0: return " "; case 1: return jEdit.getProperty("manage-plugins.info.name"); case 2: return jEdit.getProperty("manage-plugins.info.version"); case 3: return jEdit.getProperty("manage-plugins.info.status"); case 4: return jEdit.getProperty("manage-plugins.info.data"); default: throw new Error("Column out of range"); } } //}}} //{{{ getEntry() method public Entry getEntry(int rowIndex) { return entries.get(rowIndex); } //}}} //{{{ getEntry() method public Entry getEntry(String classname) { if (classname == null || classname.isEmpty()) return null; for (Entry entry : entries) { if (classname.equals(entry.clazz)) return entry; } return null; } //}}} //{{{ getRowCount() method @Override public int getRowCount() { return entries.size(); } //}}} //{{{ getValueAt() method @Override public Object getValueAt(int rowIndex,int columnIndex) { Entry entry = entries.get(rowIndex); switch (columnIndex) { case 0: return Boolean.valueOf(!entry.status.equals(Entry.NOT_LOADED) && !entry.status.equals(Entry.DISABLED)); case 1: if(entry.name == null) { return MiscUtilities.getFileName(entry.jar); } else { return entry.name; } case 2: return entry.version; case 3: return jEdit.getProperty("plugin-manager.status." + entry.status); case 4: if (entry.dataSize == null && entry.plugin != null) { File pluginDirectory = entry.plugin.getPluginHome(); if (null == pluginDirectory) { return null; } if (pluginDirectory.exists()) { entry.dataSize = StandardUtilities.formatFileSize(IOUtilities.fileLength(pluginDirectory)); } else { if (jEdit.getBooleanProperty("plugin." + entry.clazz + ".usePluginHome")) { entry.dataSize = StandardUtilities.formatFileSize(0); } else { entry.dataSize = jEdit.getProperty("manage-plugins.data-size.unknown"); } } } return entry.dataSize; default: throw new Error("Column out of range"); } } //}}} //{{{ isCellEditable() method @Override public boolean isCellEditable(int rowIndex, int columnIndex) { return columnIndex == 0; } //}}} //{{{ setValueAt() method @Override public void setValueAt(Object value, int rowIndex, int columnIndex) { Entry entry = entries.get(rowIndex); if(columnIndex == 0) { PluginJAR jar = jEdit.getPluginJAR(entry.jar); if(jar == null) { if(value.equals(Boolean.FALSE)) return; PluginJAR load = PluginJAR.load(entry.jar, true); if (load == null) { GUIUtilities.error(ManagePanel.this, "plugin-load-error", null); } } else { if(value.equals(Boolean.TRUE)) return; unloadPluginJARWithDialog(jar); } } update(); } //}}} //{{{ setSortType() method public void setSortType(int type) { sortType = type; sort(type); } //}}} //{{{ sort() method public void sort(int type) { List<String> savedSelection = new ArrayList<String>(); saveSelection(savedSelection); Collections.sort(entries,new EntryCompare(type)); fireTableChanged(new TableModelEvent(this)); restoreSelection(savedSelection); } //}}} //{{{ update() method public void update() { List<String> savedSelection = new ArrayList<String>(); saveSelection(savedSelection); entries.clear(); String systemJarDir = MiscUtilities.constructPath( jEdit.getJEditHome(),"jars"); String userJarDir; String settingsDirectory = jEdit.getSettingsDirectory(); if(settingsDirectory == null) userJarDir = null; else { userJarDir = MiscUtilities.constructPath( settingsDirectory,"jars"); } PluginJAR[] plugins = jEdit.getPluginJARs(); for(int i = 0; i < plugins.length; i++) { String path = plugins[i].getPath(); if(path.startsWith(systemJarDir) || (userJarDir != null && path.startsWith(userJarDir))) { Entry e = new Entry(plugins[i]); if(!hideLibraries.isSelected() || e.clazz != null) { entries.add(e); } } } String[] newPlugins = jEdit.getNotLoadedPluginJARs(); for(int i = 0; i < newPlugins.length; i++) { Entry e = new Entry(newPlugins[i]); entries.add(e); } sort(sortType); restoreSelection(savedSelection); } //}}} //{{{ unloadPluginJARWithDialog() method // Perhaps this should also be moved to PluginJAR class? private void unloadPluginJARWithDialog(PluginJAR jar) { // unloaded = new HashSet<String>(); unloaded = new ConcurrentHashMap<String, Object>(); String[] dependents = jar.getAllDependentPlugins(); if(dependents.length == 0) { unloadPluginJAR(jar); } else { List<String> closureSet = new LinkedList<String>(); dependents = jar.getDependentPlugins(); PluginJAR.transitiveClosure(dependents, closureSet); List<String> listModel = new ArrayList<String>(new HashSet<String>(closureSet)); // remove dupes boolean confirm = true; if (!listModel.isEmpty()) { // show confirmation dialog listing dependencies to be unloaded Collections.sort(listModel, new StandardUtilities.StringCompare<String>(true)); int button = GUIUtilities.listConfirm(window,"plugin-manager.dependency", new String[] { jar.getFile().getName() }, listModel.toArray()); confirm = button == JOptionPane.YES_OPTION; } if (confirm) { String[] optionals = jar.getOptionallyDependentPlugins(); unloadPluginJAR(jar); // reload the optionally dependent plugins since they can run // without this plugin for (String opt : optionals) { PluginJAR.load(opt, true); } } } } //}}} //{{{ unloadPluginJAR() method private void unloadPluginJAR(PluginJAR jar) { String[] dependents = jar.getAllDependentPlugins(); for (String dependent : dependents) { if (!unloaded.containsKey(dependent)) { unloaded.put(dependent, Boolean.TRUE); PluginJAR _jar = jEdit.getPluginJAR(dependent); if(_jar != null) unloadPluginJAR(_jar); } } jEdit.removePluginJAR(jar,false); jEdit.setBooleanProperty("plugin-blacklist."+MiscUtilities.getFileName(jar.getPath()),true); jEdit.propertiesChanged(); } //}}} //{{{ saveSelection() method /** * Save the selection in the given list. * The list will be filled with the jar names of the selected entries * * @param savedSelection the list where to save the selection */ public void saveSelection(List<String> savedSelection) { if (table != null) { int[] rows = table.getSelectedRows(); for (int row : rows) savedSelection.add(entries.get(row).jar); } } //}}} //{{{ restoreSelection() method /** * Restore the selection. * * @param savedSelection the selection list that contains the jar names of the selected items */ public void restoreSelection(List<String> savedSelection) { if (null != table) { table.setColumnSelectionInterval(0,0); if (!savedSelection.isEmpty()) { int i = 0; int rowCount = getRowCount(); for ( ; i<rowCount ; i++) { if (savedSelection.contains(entries.get(i).jar)) { table.setRowSelectionInterval(i,i); break; } } ListSelectionModel lsm = table.getSelectionModel(); for ( ; i<rowCount ; i++) { if (savedSelection.contains(entries.get(i).jar)) { lsm.addSelectionInterval(i,i); } } } else { if (table.getRowCount() != 0) table.setRowSelectionInterval(0,0); JScrollBar scrollbar = scrollpane.getVerticalScrollBar(); scrollbar.setValue(scrollbar.getMinimum()); } } } //}}} } //}}} //{{{ TextRenderer class private class TextRenderer extends DefaultTableCellRenderer { private final DefaultTableCellRenderer tcr; TextRenderer(DefaultTableCellRenderer tcr) { this.tcr = tcr; } @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { Entry entry = pluginModel.getEntry(row); if (entry.status.equals(Entry.ERROR) || entry.status.equals(Entry.DISABLED)) tcr.setForeground(Color.red); else tcr.setForeground(UIManager.getColor("Table.foreground")); return tcr.getTableCellRendererComponent(table,value,isSelected,false,row,column); } } //}}} //{{{ HideLibrariesButton class private class HideLibrariesButton extends JCheckBox implements ActionListener { HideLibrariesButton() { super(jEdit.getProperty("plugin-manager.hide-libraries")); setSelected(jEdit.getBooleanProperty( "plugin-manager.hide-libraries.toggle")); addActionListener(this); } @Override public void actionPerformed(ActionEvent evt) { jEdit.setBooleanProperty( "plugin-manager.hide-libraries.toggle", isSelected()); ManagePanel.this.update(); } } //}}} //{{{ RestoreButton class /** * Permits the user to restore the state of the ManagePanel * based on a PluginSet. * * Selects all loaded plugins that appear in an .XML file, and deselects * all others, and also sets the pluginset to that .XML file. Does not install any plugins * that were not previously installed. * * @since jEdit 4.3pre10 * @author Alan Ezust */ private class RestoreButton extends RolloverButton implements ActionListener { RestoreButton() { setIcon(GUIUtilities.loadIcon(jEdit.getProperty("manage-plugins.restore.icon"))); addActionListener(this); setToolTipText("Choose a PluginSet, select/deselect plugins based on set."); } @Override public void actionPerformed(ActionEvent e) { String path = jEdit.getProperty(PluginManager.PROPERTY_PLUGINSET, jEdit.getSettingsDirectory() + File.separator); String[] selectedFiles = GUIUtilities.showVFSFileDialog(ManagePanel.this.window, jEdit.getActiveView(), path, VFSBrowser.OPEN_DIALOG, false); if (selectedFiles == null || selectedFiles.length != 1) return; path = selectedFiles[0]; boolean success = loadPluginSet(path); if (success) { jEdit.setProperty(PluginManager.PROPERTY_PLUGINSET, path); EditBus.send(new PropertiesChanged(PluginManager.getInstance())); } } }//}}} //{{{ SaveButton class /** * Permits the user to save the state of the ManagePanel, * which in this case, is nothing more than a list of * all plugins currently loaded. * @since jEdit 4.3pre10 * @author Alan Ezust */ private class SaveButton extends RolloverButton implements ActionListener { SaveButton() { setIcon(GUIUtilities.loadIcon(jEdit.getProperty("manage-plugins.save.icon"))); setToolTipText("Save Currently Checked Plugins Set"); addActionListener(this); setEnabled(true); } void saveState(String vfsURL, List<Entry> pluginList) { StringBuilder sb = new StringBuilder("<pluginset>\n "); for (Entry entry: pluginList) { String jarName = entry.jar.substring(1+entry.jar.lastIndexOf(File.separatorChar)); sb.append(" <plugin name=\"").append(entry.name).append("\" jar=\""); sb.append(jarName).append("\" />\n "); } sb.append("</pluginset>\n"); VFS vfs = VFSManager.getVFSForPath(vfsURL); Object session = vfs.createVFSSession(vfsURL, ManagePanel.this); Writer writer = null; try { OutputStream os = vfs._createOutputStream(session, vfsURL, ManagePanel.this); writer = new BufferedWriter(new OutputStreamWriter(os, "utf-8")); writer.write(sb.toString()); } catch (Exception e) { Log.log(Log.ERROR, this, "Saving State Error", e); } finally { IOUtilities.closeQuietly((Closeable)writer); } } @Override public void actionPerformed(ActionEvent e) { String path = jEdit.getProperty("plugin-manager.pluginset.path", jEdit.getSettingsDirectory() + File.separator); VFSFileChooserDialog fileChooser = new VFSFileChooserDialog( ManagePanel.this.window, jEdit.getActiveView(), path, VFSBrowser.SAVE_DIALOG, false , true); String[] fileselections = fileChooser.getSelectedFiles(); List<Entry> pluginSelections = new ArrayList<Entry>(); if (fileselections == null || fileselections.length != 1) return; PluginJAR[] jars = jEdit.getPluginJARs(); for (PluginJAR jar : jars) { if (jar.getPlugin() != null) { Entry entry = new Entry (jar); pluginSelections.add(entry); } } saveState(fileselections[0], pluginSelections); jEdit.setProperty("plugin-manager.pluginset.path", fileselections[0]); EditBus.send(new PropertiesChanged(PluginManager.getInstance())); } }//}}} //{{{ RemoveButton class /** * The Remove button is the button pressed to remove the selected * plugin. */ private class RemoveButton extends JButton implements ListSelectionListener, ActionListener { RemoveButton() { super(jEdit.getProperty("manage-plugins.remove")); table.getSelectionModel().addListSelectionListener(this); addActionListener(this); setEnabled(false); } @Override public void actionPerformed(ActionEvent evt) { int[] selected = table.getSelectedRows(); List<String> listModel = new LinkedList<String>(); Roster roster = new Roster(); Set<String> jarsToRemove = new HashSet<String>(); // this one will contains the loaded jars to remove. They // are the only one we need to check to unload plugins // that depends on them Set<String> loadedJarsToRemove = new HashSet<String>(); for(int i = 0; i < selected.length; i++) { Entry entry = pluginModel.getEntry(selected[i]); if (entry.status.equals(Entry.NOT_LOADED) || entry.status.equals(Entry.DISABLED)) { if (entry.jar != null) { try { Collection<String> jarList = getDeclaredJars(entry.jar); jarsToRemove.addAll(jarList); } catch (IOException e) { Log.log(Log.ERROR, this, e); } } } else { jarsToRemove.addAll(entry.jars); loadedJarsToRemove.addAll(entry.jars); } table.getSelectionModel().removeSelectionInterval(selected[i], selected[i]); } for (String jar : jarsToRemove) { listModel.add(jar); roster.addRemove(jar); } int button = GUIUtilities.listConfirm(window, "plugin-manager.remove-confirm", null,listModel.toArray()); if(button == JOptionPane.YES_OPTION) { List<String> closureSet = new ArrayList<String>(); PluginJAR.transitiveClosure(loadedJarsToRemove.toArray(new String[loadedJarsToRemove.size()]), closureSet); closureSet.removeAll(listModel); if (closureSet.isEmpty()) { button = JOptionPane.YES_OPTION; } else { button = GUIUtilities.listConfirm(window,"plugin-manager.remove-dependencies", null, closureSet.toArray()); Collections.sort(closureSet, new StandardUtilities.StringCompare<String>(true)); } if(button == JOptionPane.YES_OPTION) { for (String jarName:closureSet) { PluginJAR pluginJAR = jEdit.getPluginJAR(jarName); jEdit.removePluginJAR(pluginJAR, false); } roster.performOperationsInAWTThread(window); pluginModel.update(); if (table.getRowCount() != 0) { table.setRowSelectionInterval(0,0); } table.setColumnSelectionInterval(0,0); JScrollBar scrollbar = scrollpane.getVerticalScrollBar(); scrollbar.setValue(scrollbar.getMinimum()); } } PluginManager.getInstance().pluginRemoved(); } @Override public void valueChanged(ListSelectionEvent e) { if (table.getSelectedRowCount() == 0) setEnabled(false); else setEnabled(true); } } //}}} //{{{ FindOrphanActionListener class private class FindOrphan extends JButton implements ActionListener { private FindOrphan() { super(jEdit.getProperty("plugin-manager.findOrphan.label")); addActionListener(this); } @Override public void actionPerformed(ActionEvent e) { PluginJAR[] pluginJARs = jEdit.getPluginJARs(); Set<String> neededJars = new HashSet<String>(); Map<String, String> jarlibs = new HashMap<String, String>(); for (PluginJAR pluginJAR : pluginJARs) { EditPlugin plugin = pluginJAR.getPlugin(); if (plugin == null) { jarlibs.put(new File(pluginJAR.getPath()).getName(), pluginJAR.getPath()); } else { Set<String> strings = plugin.getPluginJAR().getRequiredJars(); for (String string : strings) { neededJars.add(new File(string).getName()); } } } String[] notLoadedJars = jEdit.getNotLoadedPluginJARs(); for (int i = 0; i < notLoadedJars.length; i++) { PluginJAR pluginJAR = new PluginJAR(new File(notLoadedJars[i])); PluginJAR.PluginCacheEntry pluginCacheEntry = PluginJAR.getPluginCache(pluginJAR); try { if (pluginCacheEntry == null) { pluginCacheEntry = pluginJAR.generateCache(); } if(pluginCacheEntry == null) { // this happens when, for some reason, two versions // of a plugin are installed, e.g when XSLT.jar and // xslt.jar are both in $JEDIT_HOME/jars on Linux. Log.log(Log.WARNING, ManagePanel.class, "couldn't load plugin "+pluginJAR.getPath() +" (most likely other version exists)"); } if (pluginCacheEntry == null || pluginCacheEntry.pluginClass == null) { // Not a plugin jarlibs.put(new File(notLoadedJars[i]).getName(), notLoadedJars[i]); continue; } Properties cachedProperties = pluginCacheEntry.cachedProperties; String jars = cachedProperties.getProperty("plugin." + pluginCacheEntry.pluginClass + ".jars"); if (jars != null) { StringTokenizer st = new StringTokenizer(jars); while (st.hasMoreTokens()) { neededJars.add(st.nextToken()); } } } catch (IOException e1) { Log.log(Log.ERROR, this, e); } } List<String> removingJars = new ArrayList<String>(); Set<String> jarlibsKeys = jarlibs.keySet(); for (String jar : jarlibsKeys) { if (!neededJars.contains(jar)) { removingJars.add(jar); Log.log(Log.MESSAGE, this, "It seems that this jar do not belong to any plugin " +jar); } } if(removingJars.isEmpty()) { GUIUtilities.message(ManagePanel.this, "plugin-manager.noOrphan", null); return; } String[] strings = removingJars.toArray(new String[removingJars.size()]); List<String> mustRemove = new ArrayList<String>(); int ret = GUIUtilities.listConfirm(ManagePanel.this, "plugin-manager.findOrphan", null, strings, mustRemove); if (ret != JOptionPane.OK_OPTION || mustRemove.isEmpty()) return; Roster roster = new Roster(); for (String entry : mustRemove) roster.addRemove(jarlibs.get(entry)); roster.performOperationsInAWTThread(window); pluginModel.update(); if (table.getRowCount() != 0) { table.setRowSelectionInterval(0,0); } table.setColumnSelectionInterval(0,0); JScrollBar scrollbar = scrollpane.getVerticalScrollBar(); scrollbar.setValue(scrollbar.getMinimum()); table.repaint(); } } //}}} //{{{ HelpButton class private class HelpButton extends JButton implements ListSelectionListener, ActionListener { private URL docURL; HelpButton() { super(jEdit.getProperty("manage-plugins.help")); table.getSelectionModel().addListSelectionListener(this); addActionListener(this); setEnabled(false); } @Override public void actionPerformed(ActionEvent evt) { new HelpViewer(docURL); } @Override public void valueChanged(ListSelectionEvent e) { if (table.getSelectedRowCount() == 1) { try { Entry entry = pluginModel.getEntry(table.getSelectedRow()); String label = entry.clazz; String docs = entry.docs; if (label != null) { EditPlugin plug = jEdit.getPlugin(label, false); PluginJAR jar = null; if (plug != null) jar = plug.getPluginJAR(); if(jar != null && docs != null) { URL url = jar.getClassLoader().getResource(docs); if(url != null) { docURL = url; setEnabled(true); return; } } } } catch (Exception ex) { Log.log(Log.ERROR, this, "ManagePanel HelpButton Update", ex); } } setEnabled(false); } } //}}} //{{{ EntryCompare class private static class EntryCompare implements Comparator<Entry> { public static final int NAME = 1; public static final int STATUS = 2; private final int type; EntryCompare(int type) { this.type = type; } @Override public int compare(Entry e1, Entry e2) { if (type == NAME) return compareNames(e1,e2); else { int result; if ((result = e1.status.compareToIgnoreCase(e2.status)) == 0) return compareNames(e1,e2); return result; } } private static int compareNames(Entry e1, Entry e2) { String s1; if(e1.name == null) s1 = MiscUtilities.getFileName(e1.jar); else s1 = e1.name; String s2; if(e2.name == null) s2 = MiscUtilities.getFileName(e2.jar); else s2 = e2.name; return s1.compareToIgnoreCase(s2); } } //}}} //{{{ HeaderMouseHandler class private class HeaderMouseHandler extends MouseAdapter { @Override public void mouseClicked(MouseEvent evt) { if (evt.getSource() == table.getTableHeader()) { switch(table.getTableHeader().columnAtPoint(evt.getPoint())) { case 1: pluginModel.setSortType(EntryCompare.NAME); break; case 3: pluginModel.setSortType(EntryCompare.STATUS); break; default: break; } } else { if (GUIUtilities.isPopupTrigger(evt)) { int row = table.rowAtPoint(evt.getPoint()); if (row != -1 && !table.isRowSelected(row)) { table.setRowSelectionInterval(row,row); } if (popup == null) { popup = new JPopupMenu(); JMenuItem item = GUIUtilities.loadMenuItem("plugin-manager.cleanup"); item.addActionListener(new CleanupActionListener()); popup.add(item); } GUIUtilities.showPopupMenu(popup, table, evt.getX(), evt.getY()); } } } //{{{ CleanupActionListener class private class CleanupActionListener implements ActionListener { @Override public void actionPerformed(ActionEvent e) { int[] ints = table.getSelectedRows(); List<String> list = new ArrayList<String>(ints.length); List<Entry> entries = new ArrayList<Entry>(ints.length); for (int i = 0; i < ints.length; i++) { Entry entry = pluginModel.getEntry(ints[i]); if (entry.plugin != null) { list.add(entry.name); entries.add(entry); } } String[] strings = list.toArray(new String[list.size()]); int ret = GUIUtilities.listConfirm(ManagePanel.this, "plugin-manager.cleanup", null, strings); if (ret != JOptionPane.OK_OPTION) return; for (int i = 0; i < entries.size(); i++) { Entry entry = entries.get(i); File path = entry.plugin.getPluginHome(); Log.log(Log.NOTICE, this, "Removing data of plugin " + entry.name + " home="+path); FileVFS.recursiveDelete(path); entry.dataSize = null; } table.repaint(); } } //}}} } //}}} //{{{ KeyboardAction class private class KeyboardAction extends AbstractAction { private KeyboardCommand command = KeyboardCommand.NONE; KeyboardAction(KeyboardCommand command) { this.command = command; } @Override public void actionPerformed(ActionEvent evt) { switch (command) { case TAB_OUT_FORWARD: KeyboardFocusManager.getCurrentKeyboardFocusManager().focusNextComponent(); break; case TAB_OUT_BACK: KeyboardFocusManager.getCurrentKeyboardFocusManager().focusPreviousComponent(); break; case EDIT_PLUGIN: int[] rows = table.getSelectedRows(); for (int row : rows) { Object st = pluginModel.getValueAt(row, 0); pluginModel.setValueAt( st.equals(Boolean.FALSE), row, 0); } break; case CLOSE_PLUGIN_MANAGER: window.ok(); break; default: throw new InternalError(); } } } //}}} //{{{ TableFocusHandler class private class TableFocusHandler extends FocusAdapter { @Override public void focusGained(FocusEvent fe) { if (table.getSelectedRow() == -1) { table.setRowSelectionInterval(0,0); JScrollBar scrollbar = scrollpane.getVerticalScrollBar(); scrollbar.setValue(scrollbar.getMinimum()); } if (table.getSelectedColumn() == -1) { table.setColumnSelectionInterval(0,0); } } } //}}} //{{{ TableSelectionListener class private class TableSelectionListener implements ListSelectionListener { @Override public void valueChanged(ListSelectionEvent e) { int row = table.getSelectedRow(); if (row != -1) { Entry entry = pluginModel.getEntry(row); pluginDetailPanel.setPlugin(entry); } } } //}}} //}}} }