001: //The contents of this file are subject to the Mozilla Public License Version 1.1
002: //(the "License"); you may not use this file except in compliance with the
003: //License. You may obtain a copy of the License at http://www.mozilla.org/MPL/
004: //
005: //Software distributed under the License is distributed on an "AS IS" basis,
006: //WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
007: //for the specific language governing rights and
008: //limitations under the License.
009: //
010: //The Original Code is "The Columba Project"
011: //
012: //The Initial Developers of the Original Code are Frederik Dietz and Timo Stich.
013: //Portions created by Frederik Dietz and Timo Stich are Copyright (C) 2003.
014: //
015: //All Rights Reserved.
016:
017: package org.columba.core.gui.plugin;
018:
019: import java.awt.BorderLayout;
020: import java.awt.Color;
021: import java.awt.Component;
022: import java.awt.Dimension;
023: import java.awt.GridBagConstraints;
024: import java.awt.GridBagLayout;
025: import java.awt.GridLayout;
026: import java.awt.event.ActionEvent;
027: import java.awt.event.ActionListener;
028: import java.awt.event.KeyEvent;
029: import java.io.File;
030: import java.io.IOException;
031: import java.net.URL;
032: import java.util.logging.Logger;
033:
034: import javax.swing.BorderFactory;
035: import javax.swing.Box;
036: import javax.swing.BoxLayout;
037: import javax.swing.JButton;
038: import javax.swing.JComponent;
039: import javax.swing.JDialog;
040: import javax.swing.JFileChooser;
041: import javax.swing.JFrame;
042: import javax.swing.JLabel;
043: import javax.swing.JOptionPane;
044: import javax.swing.JPanel;
045: import javax.swing.JScrollPane;
046: import javax.swing.KeyStroke;
047: import javax.swing.SwingConstants;
048: import javax.swing.event.TreeSelectionEvent;
049: import javax.swing.event.TreeSelectionListener;
050: import javax.swing.filechooser.FileFilter;
051:
052: import org.columba.api.plugin.IExtensionHandler;
053: import org.columba.api.plugin.IExtensionHandlerKeys;
054: import org.columba.api.plugin.PluginHandlerNotFoundException;
055: import org.columba.api.plugin.PluginLoadingFailedException;
056: import org.columba.api.plugin.PluginMetadata;
057: import org.columba.core.config.DefaultConfigDirectory;
058: import org.columba.core.gui.base.ButtonWithMnemonic;
059: import org.columba.core.gui.base.InfoViewerDialog;
060: import org.columba.core.gui.base.SingleSideEtchedBorder;
061: import org.columba.core.help.HelpManager;
062: import org.columba.core.io.DirectoryIO;
063: import org.columba.core.io.ZipFileIO;
064: import org.columba.core.plugin.PluginManager;
065: import org.columba.core.resourceloader.GlobalResourceLoader;
066:
067: /**
068: * @author fdietz
069: *
070: * This dialog lets you view all installed plugins in a categorized tree view.
071: *
072: * There are buttons which let you: - install new plugins - remove plugins -
073: * enable/disable plugins - view plugin info (readme.txt in plugin folder)
074: */
075: public class PluginManagerDialog extends JDialog implements
076: ActionListener, TreeSelectionListener {
077: private static final String RESOURCE_PATH = "org.columba.core.i18n.dialog";
078:
079: private static final Logger LOG = Logger
080: .getLogger("org.columba.core.gui.plugin");
081:
082: protected JButton installButton;
083:
084: protected JButton removeButton;
085:
086: protected JButton optionsButton;
087:
088: protected JButton infoButton;
089:
090: protected JButton helpButton;
091:
092: protected JButton closeButton;
093:
094: protected PluginTree table;
095:
096: protected IExtensionHandler configHandler;
097:
098: protected PluginNode selectedNode;
099:
100: private static PluginManagerDialog instance;
101:
102: private PluginManagerDialog() {
103: // modal JDialog
104: super ((JFrame) null, GlobalResourceLoader.getString(
105: RESOURCE_PATH, "pluginmanager", "title"), true);
106:
107: try {
108: configHandler = PluginManager
109: .getInstance()
110: .getExtensionHandler(
111: IExtensionHandlerKeys.ORG_COLUMBA_CORE_CONFIG);
112: } catch (Exception ex) {
113: ex.printStackTrace();
114: }
115:
116: initComponents();
117: pack();
118: setLocationRelativeTo(null);
119: setVisible(true);
120: }
121:
122: public static PluginManagerDialog getInstance() {
123: if (instance == null) {
124: instance = new PluginManagerDialog();
125: }
126:
127: return instance;
128: }
129:
130: protected void initComponents() {
131: JPanel mainPanel = new JPanel();
132: mainPanel.setLayout(new BorderLayout());
133: mainPanel.setBorder(BorderFactory.createEmptyBorder(12, 12, 12,
134: 12));
135: getContentPane().add(mainPanel);
136:
137: installButton = new ButtonWithMnemonic(GlobalResourceLoader
138: .getString(RESOURCE_PATH, "pluginmanager", "install"));
139: installButton.setActionCommand("INSTALL");
140: installButton.addActionListener(this );
141:
142: removeButton = new ButtonWithMnemonic(GlobalResourceLoader
143: .getString(RESOURCE_PATH, "pluginmanager", "remove"));
144: removeButton.setActionCommand("REMOVE");
145: removeButton.setEnabled(false);
146: removeButton.addActionListener(this );
147:
148: optionsButton = new ButtonWithMnemonic(GlobalResourceLoader
149: .getString(RESOURCE_PATH, "pluginmanager", "options"));
150: optionsButton.setActionCommand("OPTIONS");
151: optionsButton.setEnabled(false);
152: optionsButton.addActionListener(this );
153:
154: infoButton = new ButtonWithMnemonic(GlobalResourceLoader
155: .getString(RESOURCE_PATH, "pluginmanager", "info"));
156: infoButton.setActionCommand("INFO");
157: infoButton.setEnabled(false);
158: infoButton.addActionListener(this );
159:
160: // top panel
161: JPanel topPanel = new JPanel();
162: topPanel.setLayout(new BoxLayout(topPanel, BoxLayout.X_AXIS));
163:
164: GridBagLayout gridBagLayout = new GridBagLayout();
165: GridBagConstraints c = new GridBagConstraints();
166:
167: // topPanel.setLayout( );
168: JPanel topBorderPanel = new JPanel();
169: topBorderPanel.setLayout(new BorderLayout());
170:
171: // topBorderPanel.setBorder(BorderFactory.createEmptyBorder(0, 0, 5,
172: // 0));
173: topBorderPanel.add(topPanel);
174:
175: // mainPanel.add( topBorderPanel, BorderLayout.NORTH );
176: JLabel nameLabel = new JLabel("name");
177: nameLabel.setEnabled(false);
178: topPanel.add(nameLabel);
179:
180: topPanel.add(Box.createRigidArea(new Dimension(10, 0)));
181: topPanel.add(Box.createHorizontalGlue());
182:
183: Component glue = Box.createVerticalGlue();
184: c.anchor = GridBagConstraints.EAST;
185: c.gridwidth = GridBagConstraints.REMAINDER;
186:
187: // c.fill = GridBagConstraints.HORIZONTAL;
188: gridBagLayout.setConstraints(glue, c);
189:
190: gridBagLayout = new GridBagLayout();
191: c = new GridBagConstraints();
192:
193: JPanel eastPanel = new JPanel(gridBagLayout);
194: mainPanel.add(eastPanel, BorderLayout.EAST);
195:
196: c.fill = GridBagConstraints.HORIZONTAL;
197: c.weightx = 1.0;
198: c.gridwidth = GridBagConstraints.REMAINDER;
199: gridBagLayout.setConstraints(installButton, c);
200: eastPanel.add(installButton);
201:
202: Component strut1 = Box.createRigidArea(new Dimension(30, 5));
203: gridBagLayout.setConstraints(strut1, c);
204: eastPanel.add(strut1);
205:
206: gridBagLayout.setConstraints(removeButton, c);
207: eastPanel.add(removeButton);
208:
209: Component strut = Box.createRigidArea(new Dimension(30, 5));
210: gridBagLayout.setConstraints(strut, c);
211: eastPanel.add(strut);
212:
213: gridBagLayout.setConstraints(optionsButton, c);
214: eastPanel.add(optionsButton);
215:
216: Component strut3 = Box.createRigidArea(new Dimension(30, 5));
217: gridBagLayout.setConstraints(strut3, c);
218: eastPanel.add(strut3);
219:
220: gridBagLayout.setConstraints(infoButton, c);
221: eastPanel.add(infoButton);
222:
223: strut = Box.createRigidArea(new Dimension(30, 20));
224: gridBagLayout.setConstraints(strut, c);
225: eastPanel.add(strut);
226:
227: glue = Box.createVerticalGlue();
228: c.fill = GridBagConstraints.BOTH;
229: c.weighty = 1.0;
230: gridBagLayout.setConstraints(glue, c);
231: eastPanel.add(glue);
232:
233: // centerpanel
234: JPanel centerPanel = new JPanel(new BorderLayout());
235: centerPanel.setBorder(BorderFactory.createEmptyBorder(0, 0, 0,
236: 5));
237:
238: /*
239: * listView = new FilterListTable(filterList, this);
240: * listView.getSelectionModel().addListSelectionListener(this);
241: * JScrollPane scrollPane = new JScrollPane(listView);
242: * scrollPane.setPreferredSize(new Dimension(300, 250));
243: * scrollPane.getViewport().setBackground(Color.white);
244: * centerPanel.add(scrollPane);
245: */
246: table = new PluginTree();
247: table.getTree().addTreeSelectionListener(this );
248:
249: JScrollPane scrollPane = new JScrollPane(table);
250: scrollPane.setPreferredSize(new Dimension(350, 300));
251: scrollPane.getViewport().setBackground(Color.white);
252: centerPanel.add(scrollPane);
253:
254: mainPanel.add(centerPanel);
255:
256: JPanel bottomPanel = new JPanel(new BorderLayout());
257: bottomPanel.setBorder(new SingleSideEtchedBorder(
258: SwingConstants.TOP));
259:
260: JPanel buttonPanel = new JPanel(new GridLayout(1, 3, 6, 0));
261: buttonPanel.setBorder(BorderFactory.createEmptyBorder(12, 12,
262: 12, 12));
263:
264: JButton closeButton = new ButtonWithMnemonic(
265: GlobalResourceLoader.getString("global", "global",
266: "close"));
267: closeButton.setActionCommand("CLOSE");
268: closeButton.addActionListener(this );
269: buttonPanel.add(closeButton);
270:
271: ButtonWithMnemonic helpButton = new ButtonWithMnemonic(
272: GlobalResourceLoader.getString("global", "global",
273: "help"));
274: buttonPanel.add(helpButton);
275: bottomPanel.add(buttonPanel, BorderLayout.EAST);
276: getContentPane().add(bottomPanel, BorderLayout.SOUTH);
277: getRootPane().setDefaultButton(closeButton);
278: getRootPane().registerKeyboardAction(this , "CLOSE",
279: KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0),
280: JComponent.WHEN_IN_FOCUSED_WINDOW);
281:
282: // associate with JavaHelp
283: HelpManager.getInstance().enableHelpOnButton(helpButton,
284: "extending_columba_1");
285: HelpManager.getInstance().enableHelpKey(getRootPane(),
286: "extending_columba_1");
287: }
288:
289: public void actionPerformed(ActionEvent e) {
290: String action = e.getActionCommand();
291:
292: if (action.equals("CLOSE")) {
293: setVisible(false);
294: } else if (action.equals("INFO")) {
295: String id = selectedNode.getId();
296:
297: URL url = PluginManager.getInstance().getInfoURL(id);
298: if (url != null) {
299: try {
300: new InfoViewerDialog(url);
301: } catch (IOException ioe) {
302: }
303: }
304: } else if (action.equals("OPTIONS")) {
305: String id = selectedNode.getId();
306: id = id.substring(id.lastIndexOf(".") + 1, id.length());
307:
308: try {
309: ConfigurationDialog dialog = new ConfigurationDialog(id);
310: dialog.setVisible(true);
311: } catch (PluginHandlerNotFoundException phnfe) {
312: } catch (PluginLoadingFailedException plfe) {
313: }
314: } else if (action.equals("REMOVE")) {
315: // get plugin directory
316: File directory = PluginManager.getInstance()
317: .getPluginMetadata(selectedNode.getId())
318: .getDirectory();
319:
320: // delete plugin from disk
321: DirectoryIO.delete(directory);
322:
323: // remove plugin from view
324: table.removePluginNode(selectedNode);
325: } else if (action.equals("INSTALL")) {
326: JFileChooser chooser = new JFileChooser();
327: chooser.addChoosableFileFilter(new FileFilter() {
328: public boolean accept(File file) {
329: return file.isDirectory()
330: || file.getName().toLowerCase().endsWith(
331: ".zip");
332: }
333:
334: public String getDescription() {
335: return GlobalResourceLoader.getString(
336: RESOURCE_PATH, "pluginmanager",
337: "filefilter");
338: }
339: });
340: chooser.setAcceptAllFileFilterUsed(false);
341:
342: int result = chooser.showOpenDialog(this );
343:
344: if (result == JFileChooser.APPROVE_OPTION) {
345: File file = chooser.getSelectedFile();
346:
347: installPlugin(file);
348: }
349: }
350: }
351:
352: public void valueChanged(TreeSelectionEvent arg0) {
353: selectedNode = (PluginNode) arg0.getPath()
354: .getLastPathComponent();
355:
356: if (selectedNode == null) {
357: return;
358: }
359:
360: boolean isCategoryFolder = selectedNode.isCategory();
361:
362: if (isCategoryFolder) {
363: // this is just a folder
364: // ->disable all actions
365: removeButton.setEnabled(false);
366: infoButton.setEnabled(false);
367:
368: optionsButton.setEnabled(false);
369: } else {
370: removeButton.setEnabled(true);
371: infoButton.setEnabled(selectedNode.hasInfo());
372:
373: if (selectedNode == null) {
374: return;
375: }
376:
377: // if plugin has config extension point
378: String id = selectedNode.getId();
379: id = id.substring(id.lastIndexOf(".") + 1, id.length());
380: optionsButton.setEnabled(configHandler.exists(id));
381: }
382: }
383:
384: /**
385: * Returns the currently selected node or null if none is selected.
386: */
387: public PluginNode getSelectedNode() {
388: return selectedNode;
389: }
390:
391: /**
392: * TODO @author fdietz: move some logic out of this ui class and into plugin package
393: */
394: protected void installPlugin(File file) {
395: // use user's config folder in his/her home-folder
396: // all plugins reside in "<config-folder>/plugins"
397: File destination = new File(DefaultConfigDirectory
398: .getInstance().getCurrentPath(), "plugins");
399:
400: LOG.info("extract " + file.getName() + " to "
401: + destination.getAbsolutePath());
402:
403: File pluginDirectory;
404: try {
405: // extract plugin
406: ZipFileIO.extract(file, destination);
407:
408: pluginDirectory = ZipFileIO.getFirstFile(file);
409: } catch (IOException ioe) {
410: JOptionPane.showMessageDialog(this , GlobalResourceLoader
411: .getString(RESOURCE_PATH, "pluginmanager",
412: "errExtract.msg"), GlobalResourceLoader
413: .getString(RESOURCE_PATH, "pluginmanager",
414: "errExtract.title"),
415: JOptionPane.ERROR_MESSAGE);
416: return;
417: }
418:
419: if (pluginDirectory != null) {
420: // the plugin directory is "<config-folder>/plugins/<plugin-id>"
421: pluginDirectory = new File(destination, pluginDirectory
422: .getName());
423: LOG.info("plugin directory="
424: + pluginDirectory.getAbsolutePath());
425:
426: // the path to the plugin.xml descriptor file is:
427: // "<config-folder>/plugins/<plugin-id>/plugin.xml
428: pluginDirectory = new File(pluginDirectory, "plugin.xml");
429:
430: String id = PluginManager.getInstance().addPlugin(
431: pluginDirectory);
432: PluginMetadata metadata = PluginManager.getInstance()
433: .getPluginMetadata(id);
434:
435: table.addPlugin(metadata);
436:
437: JOptionPane.showMessageDialog(this , GlobalResourceLoader
438: .getString(RESOURCE_PATH, "pluginmanager",
439: "installSuccess.msg"), GlobalResourceLoader
440: .getString(RESOURCE_PATH, "pluginmanager",
441: "installSuccess.title"),
442: JOptionPane.INFORMATION_MESSAGE);
443: }
444: }
445: }
|