diff --git a/app/src/processing/app/Base.java b/app/src/processing/app/Base.java index 2ff02a5e0..401def98c 100644 --- a/app/src/processing/app/Base.java +++ b/app/src/processing/app/Base.java @@ -31,6 +31,7 @@ import javax.swing.*; import processing.app.debug.Compiler; import processing.app.debug.Target; +import processing.app.tools.ZipDeflater; import processing.core.*; import static processing.app.I18n._; @@ -2362,4 +2363,25 @@ public class Base { } } } + + + public void handleAddZipLibrary(Editor editor) { + String prompt = _("Select a zip file containing the library you'd like to add"); + FileDialog fd = new FileDialog(editor, prompt, FileDialog.LOAD); + fd.setDirectory(System.getProperty("user.home")); + fd.setVisible(true); + + String directory = fd.getDirectory(); + String filename = fd.getFile(); + if (filename == null) return; + + File sourceFile = new File(directory, filename); + try { + ZipDeflater zipDeflater = new ZipDeflater(sourceFile, getSketchbookLibrariesFolder()); + zipDeflater.deflate(); + editor.statusNotice(_("Library added to your libraries. Check \"Import library\" menu")); + } catch (IOException e) { + editor.statusError(e); + } + } } diff --git a/app/src/processing/app/Editor.java b/app/src/processing/app/Editor.java index 838ca2dc2..cb18b0e44 100644 --- a/app/src/processing/app/Editor.java +++ b/app/src/processing/app/Editor.java @@ -177,7 +177,7 @@ public class Editor extends JFrame implements RunnerListener { // re-add the sub-menus that are shared by all windows fileMenu.insert(sketchbookMenu, 2); fileMenu.insert(examplesMenu, 3); - sketchMenu.insert(importMenu, 4); + //sketchMenu.insert(importMenu, 4); toolsMenu.insert(boardsMenu, numTools); toolsMenu.insert(serialMenu, numTools + 1); } @@ -188,7 +188,7 @@ public class Editor extends JFrame implements RunnerListener { // System.err.println("deactivate"); // not coming through fileMenu.remove(sketchbookMenu); fileMenu.remove(examplesMenu); - sketchMenu.remove(importMenu); + //sketchMenu.remove(importMenu); toolsMenu.remove(boardsMenu); toolsMenu.remove(serialMenu); } @@ -631,6 +631,16 @@ public class Editor extends JFrame implements RunnerListener { } sketchMenu.add(importMenu); + item = new JMenuItem(_("Add Library from ZIP")); + item.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + base.handleAddZipLibrary(Editor.this); + base.onBoardOrPortChange(); + base.rebuildImportMenu(Editor.importMenu); + } + }); + sketchMenu.add(item); + item = newJMenuItem(_("Show Sketch Folder"), 'K'); item.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { diff --git a/app/src/processing/app/tools/ZipDeflater.java b/app/src/processing/app/tools/ZipDeflater.java new file mode 100644 index 000000000..651ff37ba --- /dev/null +++ b/app/src/processing/app/tools/ZipDeflater.java @@ -0,0 +1,97 @@ +package processing.app.tools; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Enumeration; +import java.util.Random; +import java.util.zip.ZipEntry; +import java.util.zip.ZipException; +import java.util.zip.ZipFile; + +public class ZipDeflater { + + private final ZipFile zipFile; + private final File destFolder; + + public ZipDeflater(File file, File destFolder) throws ZipException, IOException { + this.destFolder = destFolder; + this.zipFile = new ZipFile(file); + } + + public void deflate() throws IOException { + String folderName = tempFolderNameFromZip(); + + File folder = new File(destFolder, folderName); + + if (!folder.mkdir()) { + throw new IOException("Unable to create folder " + folderName); + } + + Enumeration entries = zipFile.entries(); + while (entries.hasMoreElements()) { + ZipEntry entry = entries.nextElement(); + ensureFoldersOfEntryExist(folder, entry); + File entryFile = new File(folder, entry.getName()); + if (entry.isDirectory()) { + entryFile.mkdir(); + } else { + FileOutputStream fos = null; + InputStream zipInputStream = null; + try { + fos = new FileOutputStream(entryFile); + zipInputStream = zipFile.getInputStream(entry); + byte[] buffer = new byte[1024 * 4]; + int len = -1; + while ((len = zipInputStream.read(buffer)) != -1) { + fos.write(buffer, 0, len); + } + } finally { + if (fos != null) { + fos.close(); + } + if (zipInputStream != null) { + zipInputStream.close(); + } + } + } + } + + // Test.zip may or may not contain Test folder. We use zip name to create libraries folder. Therefore, a contained Test folder is useless and must be removed + ensureOneLevelFolder(folder); + } + + private void ensureFoldersOfEntryExist(File folder, ZipEntry entry) { + String[] parts = entry.getName().split("/"); + File current = folder; + for (int i = 0; i < parts.length - 1; i++) { + current = new File(current, parts[i]); + current.mkdir(); + } + } + + private void ensureOneLevelFolder(File folder) { + File[] files = folder.listFiles(); + if (files.length == 1 && files[0].isDirectory()) { + File tempFile = new File(files[0].getPath() + new Random().nextInt(1000)); + files[0].renameTo(tempFile); + for (File file : tempFile.listFiles()) { + file.renameTo(new File(folder, file.getName())); + } + tempFile.delete(); + } + } + + private String tempFolderNameFromZip() { + String folderName = zipFile.getName(); + if (folderName.lastIndexOf(".") != -1) { + folderName = folderName.substring(0, folderName.lastIndexOf(".")); + } + if (folderName.lastIndexOf(File.separator) != -1) { + folderName = folderName.substring(folderName.lastIndexOf(File.separator) + 1); + } + return folderName; + } + +}