/*******************************************************************************
* Rhythos Editor is a game editor and project management tool for making RPGs on top of the Rhythos Game system.
*
* Copyright (C) 2013 David Maletz
*
* 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 3 of the License, or
* (at your option) 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, see <http://www.gnu.org/licenses/>.
******************************************************************************/
package mrpg.plugin;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Toolkit;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.jar.Attributes;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.table.AbstractTableModel;
import mrpg.editor.MapEditor;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
public class PluginManager extends JFrame implements ActionListener {
private static final long serialVersionUID = -7566025672296138929L;
public static class PluginData {
private final File file; private Plugin plugin; private int installed;
public final String name, desc, author, version; private final String main;
public PluginData(File f) throws Exception {
file = f; plugin = null; installed = 0;
JarFile jar = new JarFile(file); Manifest m = jar.getManifest(); Attributes a = m.getMainAttributes();
name = get(a, "Plugin-Name"); desc = get(a, "Plugin-Desc"); author = get(a, "Plugin-Author");
version = get(a, "Plugin-Version"); main = a.getValue("Plugin-Main").toString();
}
private String get(Attributes a, String s){Object o = a.getValue(s); return (o == null)?"":o.toString();}
public Plugin getPlugin() throws Exception {
if(plugin == null){
URLClassLoader loader = URLClassLoader.newInstance(new URL[]{file.toURI().toURL()}, PluginManager.class.getClassLoader());
plugin = (Plugin)loader.loadClass(main).newInstance();
} return plugin;
}
public boolean isInstalled(){return installed == 1 || installed == -1;}
public void install(JFrame f){
if(installed == 0) try{getPlugin().install(f==null); installed = 1;}catch(Exception e){
if(f != null){installed = 2; JOptionPane.showMessageDialog(f, "The plugin will be installed the next time Rhythos is restarted.", "Plugin Install", JOptionPane.INFORMATION_MESSAGE);}
}
}
public void uninstall(JFrame f){
if(installed == 1) try{getPlugin().uninstall(); installed = 0; plugin = null;}catch(Exception e){
installed = -1; JOptionPane.showMessageDialog(f, "The plugin will be uninstalled the next time Rhythos is restarted.", "Plugin Uninstall", JOptionPane.INFORMATION_MESSAGE);
}
}
}
private static HashMap<String, PluginData> plugins = new HashMap<String, PluginData>();
private PluginManager(){
super("Plugin Manager"); setIconImages(MapEditor.getWindowIcon()); setSize(640,380);
setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE); MapEditor.addFrame("plugins", this);
JTable table = new JTable(new PluginTableModel(this)); table.setFillsViewportHeight(true); JPanel p = new JPanel(new BorderLayout());
p.add(new JScrollPane(table)); setContentPane(p); JMenuBar bar = new JMenuBar(); JMenu menu = new JMenu("Plugins");
JMenuItem item = new JMenuItem("Refresh Plugins"); item.addActionListener(this); menu.add(item); bar.add(menu); setJMenuBar(bar);
table.getColumnModel().getColumn(0).setPreferredWidth(150); table.getColumnModel().getColumn(1).setPreferredWidth(300);
table.getColumnModel().getColumn(2).setPreferredWidth(75); table.getColumnModel().getColumn(3).setPreferredWidth(50);
table.getColumnModel().getColumn(4).setMinWidth(60); table.getColumnModel().getColumn(4).setMaxWidth(60);
}
public void actionPerformed(ActionEvent e){scanPluginDirectory(); repaint();}
public static void openManager(){if(manager == null) manager = new PluginManager(); manager.setVisible(true);}
private static JFrame initWindow, manager;
public static void showInitWindow(){
if(initWindow != null) return; try{
initWindow = new JFrame(); initWindow.setUndecorated(true); JPanel p = new JPanel(null);
initWindow.setIconImages(MapEditor.getWindowIcon());
p.setOpaque(false); JLabel l = new JLabel(new ImageIcon(PluginManager.class.getResource("/data/splash.png")));
Dimension w = l.getPreferredSize(); l.setBounds(0,0,w.width,w.height); p.add(l);
l = new JLabel(); l.setBounds(271,334,314,26); l.setHorizontalAlignment(JLabel.CENTER);
l.setBackground(Color.WHITE); l.setOpaque(true); l.setFont(l.getFont().deriveFont(18.f)); p.add(l);
initWindow.setBackground(new Color(0,true));
try{
Class<?> awtUtilsClass = Class.forName("com.sun.awt.AWTUtilities");
if(awtUtilsClass != null){
Method method = awtUtilsClass.getMethod("setWindowOpaque", Window.class, boolean.class);
method.invoke(null, initWindow, false);
}
}catch(Exception e){}
initWindow.setContentPane(p); initWindow.setSize(w); Dimension s = Toolkit.getDefaultToolkit().getScreenSize();
initWindow.setLocation((s.width-w.width)>>1, ((s.height-w.height)>>1)-50); initWindow.setVisible(true);
} catch(Exception e){e.printStackTrace();}
}
public static void setInitStatus(String s){
if(initWindow != null) ((JLabel)initWindow.getContentPane().getComponent(1)).setText(s);
}
public static void closeInitWindow(){if(initWindow != null){initWindow.dispose(); initWindow = null;}}
public static void scanPluginDirectory(){
try{
for(File f : new File("plugins").listFiles()) try{
String n = f.getName(); if(n.endsWith(".jar")){
n = n.substring(0, n.length()-4); if(!plugins.containsKey(n)) try{
plugins.put(n, new PluginData(f));
} catch(Exception e){}
}} catch(Exception e){}
}catch(Exception ex){}
}
public static void installPlugins(Document doc, Element root){
setInitStatus("Loading Plugins..."); scanPluginDirectory(); NodeList list = root.getElementsByTagName("plugin");
for(int i=0; i<list.getLength(); i++){
try{
Element element = (Element)list.item(i); PluginData data = plugins.get(element.getAttribute("name"));
data.install(null); data.plugin.readSettings(doc, element);
}catch(Exception ex){}
} closeInitWindow();
}
public static void getInstalledPlugins(Document doc, Element root){
Iterator<Map.Entry<String, PluginData> > i = plugins.entrySet().iterator();
while(i.hasNext()){ Map.Entry<String, PluginData> e = i.next(); PluginData plugin = e.getValue(); if(plugin.installed > 0){
Element element = doc.createElement("plugin"); element.setAttribute("name", e.getKey());
if(plugin.plugin != null)
try{plugin.plugin.saveSettings(doc, element);}catch(Exception ex){} root.appendChild(element);
}}
}
private static class PluginTableModel extends AbstractTableModel {
private static final long serialVersionUID = -4382784638118928931L;
private static Class<?> colClass[] = {String.class, String.class, String.class, String.class, Boolean.class};
private static String colNames[] = {"Name", "Description", "Author", "Version", "Installed?"};
private JFrame frame;
public PluginTableModel(JFrame f){frame = f;}
public int getRowCount(){return plugins.size();}
public int getColumnCount(){return colClass.length;}
public String getColumnName(int col){return colNames[col];}
public Class<?> getColumnClass(int col){return colClass[col];}
public boolean isCellEditable(int row, int col){return col == 4;}
public void setValueAt(Object val, int row, int col){
if(col != 4) return; for(PluginData p : plugins.values()){
if(row == 0){
if(p.installed == 0){
p.install(frame); fireTableCellUpdated(row, col);
} else if(p.installed == 1){
p.uninstall(frame); fireTableCellUpdated(row, col);
} break;
} row--;
}
}
public Object getValueAt(int row, int col){
for(PluginData p : plugins.values()){
if(row == 0){
switch(col){
case 0: return p.name;
case 1: return p.desc;
case 2: return p.author;
case 3: return p.version;
case 4: return p.isInstalled();
default: return null;
}
} row--;
} return null;
}
}
}