mirror of
https://github.com/arduino/Arduino.git
synced 2025-02-26 20:54:22 +01:00
Library installer UI
This commit is contained in:
parent
0b9223c158
commit
0755c7c004
@ -0,0 +1,293 @@
|
||||
/*
|
||||
* This file is part of Arduino.
|
||||
*
|
||||
* Copyright 2014 Arduino LLC (http://www.arduino.cc/)
|
||||
*
|
||||
* Arduino 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
|
||||
* (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, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* As a special exception, you may use this file as part of a free software
|
||||
* library without restriction. Specifically, if other files instantiate
|
||||
* templates or use macros or inline functions from this file, or you compile
|
||||
* this file and link it with other files to produce an executable, this
|
||||
* file does not by itself cause the resulting executable to be covered by
|
||||
* the GNU General Public License. This exception does not however
|
||||
* invalidate any other reasons why the executable file might be covered by
|
||||
* the GNU General Public License.
|
||||
*/
|
||||
package cc.arduino.libraries.contributions.ui;
|
||||
|
||||
import static processing.app.I18n._;
|
||||
import static processing.app.I18n.format;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Component;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Insets;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
|
||||
import javax.swing.AbstractCellEditor;
|
||||
import javax.swing.Box;
|
||||
import javax.swing.BoxLayout;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JTable;
|
||||
import javax.swing.JTextPane;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
import javax.swing.event.HyperlinkEvent;
|
||||
import javax.swing.event.HyperlinkListener;
|
||||
import javax.swing.table.TableCellEditor;
|
||||
import javax.swing.table.TableCellRenderer;
|
||||
import javax.swing.text.BadLocationException;
|
||||
import javax.swing.text.Document;
|
||||
import javax.swing.text.html.HTMLDocument;
|
||||
import javax.swing.text.html.StyleSheet;
|
||||
|
||||
import processing.app.Base;
|
||||
import cc.arduino.libraries.contributions.ContributedLibrary;
|
||||
import cc.arduino.libraries.contributions.ui.LibrariesIndexTableModel.ContributedLibraryReleases;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class ContributedLibraryTableCell extends AbstractCellEditor implements
|
||||
TableCellEditor, TableCellRenderer {
|
||||
|
||||
private JPanel panel;
|
||||
private JTextPane description;
|
||||
private JButton installButton;
|
||||
private JButton removeButton;
|
||||
private Component removeButtonPlaceholder;
|
||||
private Component installButtonPlaceholder;
|
||||
|
||||
public ContributedLibraryTableCell() {
|
||||
description = new JTextPane();
|
||||
description.setInheritsPopupMenu(true);
|
||||
Insets margin = description.getMargin();
|
||||
margin.bottom = 0;
|
||||
description.setMargin(margin);
|
||||
description.setContentType("text/html");
|
||||
Document doc = description.getDocument();
|
||||
if (doc instanceof HTMLDocument) {
|
||||
HTMLDocument html = (HTMLDocument) doc;
|
||||
StyleSheet stylesheet = html.getStyleSheet();
|
||||
stylesheet.addRule("body { margin: 0; padding: 0;"
|
||||
+ "font-family: Verdana, Geneva, Arial, Helvetica, sans-serif;"
|
||||
+ "font-size: 100%;" + "font-size: 0.95em; }");
|
||||
}
|
||||
description.setOpaque(false);
|
||||
description.setBorder(new EmptyBorder(4, 7, 7, 7));
|
||||
description.setHighlighter(null);
|
||||
description.setEditable(false);
|
||||
description.addHyperlinkListener(new HyperlinkListener() {
|
||||
@Override
|
||||
public void hyperlinkUpdate(HyperlinkEvent e) {
|
||||
if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
|
||||
Base.openURL(e.getDescription());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
{
|
||||
installButton = new JButton(_("Install"));
|
||||
installButton.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
onInstall(editorValue.getSelected());
|
||||
}
|
||||
});
|
||||
int width = installButton.getPreferredSize().width;
|
||||
installButtonPlaceholder = Box.createRigidArea(new Dimension(width, 1));
|
||||
}
|
||||
|
||||
{
|
||||
removeButton = new JButton(_("Remove"));
|
||||
removeButton.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
onRemove(editorValue.getInstalled());
|
||||
}
|
||||
});
|
||||
int width = removeButton.getPreferredSize().width;
|
||||
removeButtonPlaceholder = Box.createRigidArea(new Dimension(width, 1));
|
||||
}
|
||||
|
||||
panel = new JPanel();
|
||||
panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS));
|
||||
|
||||
panel.add(description);
|
||||
panel.add(Box.createHorizontalStrut(5));
|
||||
panel.add(installButton);
|
||||
panel.add(installButtonPlaceholder);
|
||||
panel.add(Box.createHorizontalStrut(5));
|
||||
panel.add(removeButton);
|
||||
panel.add(removeButtonPlaceholder);
|
||||
panel.add(Box.createHorizontalStrut(5));
|
||||
}
|
||||
|
||||
protected void onRemove(ContributedLibrary contributedPlatform) {
|
||||
// Empty
|
||||
}
|
||||
|
||||
protected void onInstall(ContributedLibrary contributedPlatform) {
|
||||
// Empty
|
||||
}
|
||||
|
||||
public Component getTableCellRendererComponent(JTable table, Object value,
|
||||
boolean isSelected,
|
||||
boolean hasFocus, int row,
|
||||
int column) {
|
||||
parentTable = table;
|
||||
return getUpdatedCellComponent(value, isSelected, row);
|
||||
}
|
||||
|
||||
private ContributedLibraryReleases editorValue;
|
||||
private JTable parentTable;
|
||||
|
||||
@Override
|
||||
public Object getCellEditorValue() {
|
||||
return editorValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getTableCellEditorComponent(JTable table, Object value,
|
||||
boolean isSelected, int row,
|
||||
int column) {
|
||||
parentTable = table;
|
||||
editorValue = (ContributedLibraryReleases) value;
|
||||
return getUpdatedCellComponent(value, true, row);
|
||||
}
|
||||
|
||||
private Component getUpdatedCellComponent(Object value, boolean isSelected,
|
||||
int row) {
|
||||
ContributedLibraryReleases releases = (ContributedLibraryReleases) value;
|
||||
ContributedLibrary selectedLib = releases.getSelected();
|
||||
ContributedLibrary installedLib = releases.getInstalled();
|
||||
|
||||
boolean removable, installable, upgradable;
|
||||
if (installedLib == null) {
|
||||
installable = true;
|
||||
removable = false;
|
||||
upgradable = false;
|
||||
} else {
|
||||
installable = false;
|
||||
removable = !installedLib.isReadOnly();
|
||||
upgradable = (selectedLib != installedLib);
|
||||
}
|
||||
if (installable)
|
||||
installButton.setText(_("Install"));
|
||||
if (upgradable)
|
||||
installButton.setText(_("Upgrade"));
|
||||
installButton.setVisible(installable || upgradable);
|
||||
installButtonPlaceholder.setVisible(!(installable || upgradable));
|
||||
removeButton.setVisible(removable);
|
||||
removeButtonPlaceholder.setVisible(!removable);
|
||||
|
||||
String name = selectedLib.getName();
|
||||
String author = selectedLib.getAuthor();
|
||||
// String maintainer = selectedLib.getMaintainer();
|
||||
String website = selectedLib.getWebsite();
|
||||
String sentence = selectedLib.getSentence();
|
||||
String paragraph = selectedLib.getParagraph();
|
||||
String availableVer = selectedLib.getVersion();
|
||||
String url = selectedLib.getUrl();
|
||||
|
||||
String midcolor = isSelected ? "#000000" : "#888888";
|
||||
|
||||
String desc = "<html><body>";
|
||||
// Library name
|
||||
desc += format("<b><font size=\"+1\">{0}</font></b>", name);
|
||||
|
||||
desc += format("<font color=\"{0}\">", midcolor);
|
||||
|
||||
if (author != null && !author.isEmpty()) {
|
||||
desc += format(" by <a href=\"{0}\">{1}</a>", website, author);
|
||||
}
|
||||
desc += "<br />";
|
||||
|
||||
if (sentence != null) {
|
||||
desc += format("<b>{0}</b><br />", sentence);
|
||||
if (paragraph != null && !paragraph.isEmpty())
|
||||
desc += format("{0}<br />", paragraph);
|
||||
desc += "<br />";
|
||||
}
|
||||
|
||||
desc += "</font>"; // close midcolor
|
||||
|
||||
// If the selected lib is available from repository...
|
||||
if (url != null) {
|
||||
desc += format(_("Available version: <b>{0}</b>"), availableVer);
|
||||
removeButton.setText(_("Remove"));
|
||||
} else {
|
||||
removeButton.setText(_("Delete"));
|
||||
}
|
||||
desc += "<br />";
|
||||
|
||||
if (installedLib != null) {
|
||||
String installedVer = installedLib.getVersion();
|
||||
if (installedVer == null)
|
||||
installedVer = "Legacy";
|
||||
desc += format(_("Installed version: <b>{0}</b>"), installedVer);
|
||||
if (installedLib.isReadOnly())
|
||||
desc += " " + _("(Bundled)");
|
||||
}
|
||||
desc += "<br />";
|
||||
|
||||
desc += "</body></html>";
|
||||
description.setText(desc);
|
||||
description.setBackground(Color.WHITE);
|
||||
|
||||
try {
|
||||
// for modelToView to work, the text area has to be sized. It doesn't
|
||||
// matter if it's visible or not.
|
||||
|
||||
// See:
|
||||
// http://stackoverflow.com/questions/3081210/how-to-set-jtextarea-to-have-height-that-matches-the-size-of-a-text-it-contains
|
||||
int width = parentTable.getBounds().width;
|
||||
width -= installButtonPlaceholder.getPreferredSize().width;
|
||||
width -= removeButtonPlaceholder.getPreferredSize().width;
|
||||
Dimension minimalSize = new Dimension(width, 10);
|
||||
description.setPreferredSize(minimalSize);
|
||||
description.setSize(minimalSize);
|
||||
|
||||
Rectangle r = description.modelToView(description.getDocument()
|
||||
.getLength());
|
||||
r.height += description.modelToView(0).y; // add margins
|
||||
Dimension d = new Dimension(minimalSize.width, r.y + r.height);
|
||||
description.setPreferredSize(d);
|
||||
} catch (BadLocationException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
if (isSelected) {
|
||||
panel.setBackground(parentTable.getSelectionBackground());
|
||||
panel.setForeground(parentTable.getSelectionForeground());
|
||||
} else {
|
||||
panel.setBackground(parentTable.getBackground());
|
||||
panel.setForeground(parentTable.getForeground());
|
||||
}
|
||||
|
||||
return panel;
|
||||
}
|
||||
|
||||
void setEnabled(boolean enabled) {
|
||||
installButton.setEnabled(enabled);
|
||||
removeButton.setEnabled(enabled);
|
||||
}
|
||||
|
||||
public void invalidate() {
|
||||
panel.invalidate();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,278 @@
|
||||
/*
|
||||
* This file is part of Arduino.
|
||||
*
|
||||
* Copyright 2014 Arduino LLC (http://www.arduino.cc/)
|
||||
*
|
||||
* Arduino 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
|
||||
* (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, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* As a special exception, you may use this file as part of a free software
|
||||
* library without restriction. Specifically, if other files instantiate
|
||||
* templates or use macros or inline functions from this file, or you compile
|
||||
* this file and link it with other files to produce an executable, this
|
||||
* file does not by itself cause the resulting executable to be covered by
|
||||
* the GNU General Public License. This exception does not however
|
||||
* invalidate any other reasons why the executable file might be covered by
|
||||
* the GNU General Public License.
|
||||
*/
|
||||
package cc.arduino.libraries.contributions.ui;
|
||||
|
||||
import static cc.arduino.packages.contributions.VersionComparator.VERSION_COMPARATOR;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.table.AbstractTableModel;
|
||||
|
||||
import cc.arduino.libraries.contributions.ContributedLibrary;
|
||||
import cc.arduino.libraries.contributions.LibrariesIndexer;
|
||||
import cc.arduino.packages.contributions.ContributedPackage;
|
||||
import cc.arduino.packages.contributions.ContributedPlatform;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class LibrariesIndexTableModel extends AbstractTableModel {
|
||||
|
||||
public final static int DESCRIPTION_COL = 0;
|
||||
|
||||
public static class ContributedLibraryReleases implements
|
||||
Comparable<ContributedLibraryReleases> {
|
||||
public ContributedPackage packager;
|
||||
public String name;
|
||||
public List<ContributedLibrary> releases = new ArrayList<ContributedLibrary>();
|
||||
public List<String> versions = new ArrayList<String>();
|
||||
public ContributedLibrary selected = null;
|
||||
|
||||
public ContributedLibraryReleases(ContributedLibrary library) {
|
||||
name = library.getName();
|
||||
add(library);
|
||||
}
|
||||
|
||||
public boolean shouldContain(ContributedLibrary lib) {
|
||||
if (!lib.getName().equals(name))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
public void add(ContributedLibrary library) {
|
||||
releases.add(library);
|
||||
versions.add(library.getVersion());
|
||||
selected = getLatest();
|
||||
}
|
||||
|
||||
public ContributedLibrary getInstalled() {
|
||||
for (ContributedLibrary lib : releases)
|
||||
if (lib.isInstalled())
|
||||
return lib;
|
||||
return null;
|
||||
}
|
||||
|
||||
public ContributedLibrary getLatest() {
|
||||
ContributedLibrary latest = null;
|
||||
for (ContributedLibrary lib : releases) {
|
||||
if (latest == null)
|
||||
latest = lib;
|
||||
// TODO a better version compare
|
||||
|
||||
if (VERSION_COMPARATOR.compare(lib.getVersion(), latest.getVersion()) > 0)
|
||||
latest = lib;
|
||||
}
|
||||
return latest;
|
||||
}
|
||||
|
||||
public ContributedLibrary getSelected() {
|
||||
return selected;
|
||||
}
|
||||
|
||||
public void selectVersion(String version) {
|
||||
for (ContributedLibrary lib : releases) {
|
||||
if (lib.getVersion().equals(version)) {
|
||||
selected = lib;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void select(ContributedLibrary value) {
|
||||
for (ContributedLibrary plat : releases) {
|
||||
if (plat == value) {
|
||||
selected = plat;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(ContributedLibraryReleases o) {
|
||||
return name.compareToIgnoreCase(o.name);
|
||||
}
|
||||
}
|
||||
|
||||
private List<ContributedLibraryReleases> contributions = new ArrayList<ContributedLibraryReleases>();
|
||||
|
||||
private String[] columnNames = { "Description" };
|
||||
|
||||
private Class<?>[] columnTypes = { ContributedPlatform.class };
|
||||
|
||||
private LibrariesIndexer indexer;
|
||||
|
||||
public void setIndexer(LibrariesIndexer _index) {
|
||||
indexer = _index;
|
||||
}
|
||||
|
||||
String selectedCategory = null;
|
||||
String selectedFilters[] = null;
|
||||
|
||||
public void updateIndexFilter(String category, String filters[]) {
|
||||
selectedCategory = category;
|
||||
selectedFilters = filters;
|
||||
update();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if <b>string</b> contains all the substrings in <b>set</b>. The
|
||||
* compare is case insensitive.
|
||||
*
|
||||
* @param string
|
||||
* @param set
|
||||
* @return <b>true<b> if all the strings in <b>set</b> are contained in
|
||||
* <b>string</b>.
|
||||
*/
|
||||
private boolean stringContainsAll(String string, String set[]) {
|
||||
if (set == null)
|
||||
return true;
|
||||
for (String s : set) {
|
||||
if (!string.toLowerCase().contains(s.toLowerCase()))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void addContribution(ContributedLibrary lib) {
|
||||
for (ContributedLibraryReleases contribution : contributions) {
|
||||
if (!contribution.shouldContain(lib))
|
||||
continue;
|
||||
contribution.add(lib);
|
||||
return;
|
||||
}
|
||||
|
||||
contributions.add(new ContributedLibraryReleases(lib));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getColumnCount() {
|
||||
return columnNames.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRowCount() {
|
||||
return contributions.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getColumnName(int column) {
|
||||
return columnNames[column];
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getColumnClass(int colum) {
|
||||
return columnTypes[colum];
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValueAt(Object value, int row, int col) {
|
||||
if (col == DESCRIPTION_COL) {
|
||||
fireTableCellUpdated(row, col);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValueAt(int row, int col) {
|
||||
ContributedLibraryReleases contribution = contributions.get(row);
|
||||
if (col == DESCRIPTION_COL) {
|
||||
return contribution;// .getSelected();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCellEditable(int row, int col) {
|
||||
return col == DESCRIPTION_COL;
|
||||
}
|
||||
|
||||
public List<String> getReleasesVersions(int row) {
|
||||
return contributions.get(row).versions;
|
||||
}
|
||||
|
||||
public ContributedLibraryReleases getReleases(int row) {
|
||||
return contributions.get(row);
|
||||
}
|
||||
|
||||
public ContributedLibrary getSelectedRelease(int row) {
|
||||
return contributions.get(row).getSelected();
|
||||
}
|
||||
|
||||
public void update() {
|
||||
updateContributions();
|
||||
fireTableDataChanged();
|
||||
}
|
||||
|
||||
private void applyFilterToLibrary(ContributedLibrary lib) {
|
||||
if (selectedCategory != null && !selectedCategory.isEmpty()) {
|
||||
if (lib.getCategory() == null ||
|
||||
!lib.getCategory().equals(selectedCategory))
|
||||
return;
|
||||
}
|
||||
if (!stringContainsAll(lib.getName(), selectedFilters))
|
||||
return;
|
||||
addContribution(lib);
|
||||
}
|
||||
|
||||
public void updateLibrary(ContributedLibrary lib) {
|
||||
// Find the row interested in the change
|
||||
int row = -1;
|
||||
for (ContributedLibraryReleases releases : contributions) {
|
||||
if (releases.shouldContain(lib))
|
||||
row = contributions.indexOf(releases);
|
||||
}
|
||||
|
||||
updateContributions();
|
||||
|
||||
// If the library is found in the list send update event
|
||||
// or insert event on the specific row...
|
||||
for (ContributedLibraryReleases releases : contributions) {
|
||||
if (releases.shouldContain(lib)) {
|
||||
if (row == -1) {
|
||||
row = contributions.indexOf(releases);
|
||||
fireTableRowsInserted(row, row);
|
||||
} else {
|
||||
fireTableRowsUpdated(row, row);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
// ...otherwise send a row deleted event
|
||||
fireTableRowsDeleted(row, row);
|
||||
}
|
||||
|
||||
private void updateContributions() {
|
||||
contributions.clear();
|
||||
for (ContributedLibrary l : indexer.getIndex().getLibraries())
|
||||
applyFilterToLibrary(l);
|
||||
for (ContributedLibrary l : indexer.getInstalledLibraries())
|
||||
applyFilterToLibrary(l);
|
||||
Collections.sort(contributions);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,145 @@
|
||||
/*
|
||||
* This file is part of Arduino.
|
||||
*
|
||||
* Copyright 2014 Arduino LLC (http://www.arduino.cc/)
|
||||
*
|
||||
* Arduino 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
|
||||
* (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, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* As a special exception, you may use this file as part of a free software
|
||||
* library without restriction. Specifically, if other files instantiate
|
||||
* templates or use macros or inline functions from this file, or you compile
|
||||
* this file and link it with other files to produce an executable, this
|
||||
* file does not by itself cause the resulting executable to be covered by
|
||||
* the GNU General Public License. This exception does not however
|
||||
* invalidate any other reasons why the executable file might be covered by
|
||||
* the GNU General Public License.
|
||||
*/
|
||||
package cc.arduino.libraries.contributions.ui;
|
||||
|
||||
import static processing.app.I18n._;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
|
||||
import processing.app.helpers.FileUtils;
|
||||
import cc.arduino.libraries.contributions.ContributedLibrary;
|
||||
import cc.arduino.libraries.contributions.LibrariesIndexer;
|
||||
import cc.arduino.packages.contributions.DownloadableContributionsDownloader;
|
||||
import cc.arduino.utils.ArchiveExtractor;
|
||||
import cc.arduino.utils.MultiStepProgress;
|
||||
import cc.arduino.utils.Progress;
|
||||
|
||||
public class LibraryInstaller {
|
||||
|
||||
private LibrariesIndexer indexer;
|
||||
private File stagingFolder;
|
||||
private DownloadableContributionsDownloader downloader;
|
||||
|
||||
public LibraryInstaller(LibrariesIndexer _indexer) {
|
||||
indexer = _indexer;
|
||||
stagingFolder = _indexer.getStagingFolder();
|
||||
downloader = new DownloadableContributionsDownloader(stagingFolder) {
|
||||
@Override
|
||||
protected void onProgress(Progress progress) {
|
||||
LibraryInstaller.this.onProgress(progress);
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
public void updateIndex() throws Exception {
|
||||
final MultiStepProgress progress = new MultiStepProgress(2);
|
||||
|
||||
// Step 1: Download index
|
||||
URL url = new URL("http://arduino.cc/library_index.json");
|
||||
File outputFile = indexer.getIndexFile();
|
||||
File tmpFile = new File(outputFile.getAbsolutePath() + ".tmp");
|
||||
try {
|
||||
downloader.download(url, tmpFile, progress,
|
||||
_("Downloading libraries index..."));
|
||||
} catch (InterruptedException e) {
|
||||
// Download interrupted... just exit
|
||||
return;
|
||||
}
|
||||
progress.stepDone();
|
||||
|
||||
// TODO: Check downloaded index
|
||||
|
||||
// Replace old index with the updated one
|
||||
if (outputFile.exists())
|
||||
outputFile.delete();
|
||||
if (!tmpFile.renameTo(outputFile))
|
||||
throw new Exception(
|
||||
_("An error occurred while updating libraries index!"));
|
||||
|
||||
// Step 2: Rescan index
|
||||
rescanLibraryIndex(progress);
|
||||
}
|
||||
|
||||
public void install(ContributedLibrary lib) throws Exception {
|
||||
if (lib.isInstalled())
|
||||
throw new Exception(_("Library is already installed!"));
|
||||
|
||||
final MultiStepProgress progress = new MultiStepProgress(3);
|
||||
|
||||
// Step 1: Download library
|
||||
try {
|
||||
downloader.download(lib, progress, _("Downloading library."));
|
||||
} catch (InterruptedException e) {
|
||||
// Download interrupted... just exit
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Extract to temporary folders and move to the final destination only
|
||||
// once everything is successfully unpacked. If the operation fails remove
|
||||
// all the temporary folders and abort installation.
|
||||
|
||||
// Step 2: Unpack library on the correct location
|
||||
progress.setStatus(_("Installing library..."));
|
||||
onProgress(progress);
|
||||
File destFolder = new File(indexer.getSketchbookLibrariesFolder(), lib.getName());
|
||||
destFolder.mkdirs();
|
||||
ArchiveExtractor.extract(lib.getDownloadedFile(), destFolder, 1);
|
||||
progress.stepDone();
|
||||
|
||||
// Step 3: Rescan index
|
||||
rescanLibraryIndex(progress);
|
||||
}
|
||||
|
||||
public void remove(ContributedLibrary lib) throws IOException {
|
||||
final MultiStepProgress progress = new MultiStepProgress(2);
|
||||
|
||||
// Step 1: Remove library
|
||||
progress.setStatus(_("Removing library..."));
|
||||
onProgress(progress);
|
||||
FileUtils.recursiveDelete(lib.getInstalledFolder());
|
||||
progress.stepDone();
|
||||
|
||||
// Step 2: Rescan index
|
||||
rescanLibraryIndex(progress);
|
||||
}
|
||||
|
||||
private void rescanLibraryIndex(MultiStepProgress progress)
|
||||
throws IOException {
|
||||
progress.setStatus(_("Updating list of installed libraries"));
|
||||
onProgress(progress);
|
||||
indexer.rescanLibraries();
|
||||
progress.stepDone();
|
||||
}
|
||||
|
||||
protected void onProgress(Progress progress) {
|
||||
// Empty
|
||||
}
|
||||
}
|
@ -0,0 +1,381 @@
|
||||
/*
|
||||
* This file is part of Arduino.
|
||||
*
|
||||
* Copyright 2014 Arduino LLC (http://www.arduino.cc/)
|
||||
*
|
||||
* Arduino 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
|
||||
* (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, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* As a special exception, you may use this file as part of a free software
|
||||
* library without restriction. Specifically, if other files instantiate
|
||||
* templates or use macros or inline functions from this file, or you compile
|
||||
* this file and link it with other files to produce an executable, this
|
||||
* file does not by itself cause the resulting executable to be covered by
|
||||
* the GNU General Public License. This exception does not however
|
||||
* invalidate any other reasons why the executable file might be covered by
|
||||
* the GNU General Public License.
|
||||
*/
|
||||
package cc.arduino.libraries.contributions.ui;
|
||||
|
||||
import static cc.arduino.packages.contributions.ui.ContributionIndexTableModel.DESCRIPTION_COL;
|
||||
import static processing.app.I18n._;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Component;
|
||||
import java.awt.Container;
|
||||
import java.awt.Dialog;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Frame;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.util.Collection;
|
||||
|
||||
import javax.swing.Box;
|
||||
import javax.swing.BoxLayout;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JComboBox;
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JTable;
|
||||
import javax.swing.ListSelectionModel;
|
||||
import javax.swing.ScrollPaneConstants;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
import javax.swing.event.TableModelEvent;
|
||||
import javax.swing.event.TableModelListener;
|
||||
import javax.swing.table.TableCellEditor;
|
||||
import javax.swing.table.TableCellRenderer;
|
||||
import javax.swing.table.TableColumn;
|
||||
import javax.swing.table.TableColumnModel;
|
||||
|
||||
import processing.app.Theme;
|
||||
import cc.arduino.libraries.contributions.ContributedLibrary;
|
||||
import cc.arduino.libraries.contributions.LibrariesIndexer;
|
||||
import cc.arduino.ui.FilterJTextField;
|
||||
import cc.arduino.ui.ProgressJProgressBar;
|
||||
import cc.arduino.utils.Progress;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class LibraryManagerUI extends JDialog {
|
||||
|
||||
private FilterJTextField filterField;
|
||||
|
||||
private JLabel categoryLabel;
|
||||
private JComboBox categoryChooser;
|
||||
private Component categoryStrut1;
|
||||
private Component categoryStrut2;
|
||||
private Component categoryStrut3;
|
||||
|
||||
private LibrariesIndexTableModel contribModel = new LibrariesIndexTableModel();
|
||||
private JTable contribTable;
|
||||
private ProgressJProgressBar progressBar;
|
||||
|
||||
private Box progressBox;
|
||||
private Box updateBox;
|
||||
|
||||
private ContributedLibraryTableCell cellEditor;
|
||||
|
||||
// Currently selected category and filters
|
||||
private String category;
|
||||
private String[] filters;
|
||||
|
||||
public LibraryManagerUI(Frame parent) {
|
||||
super(parent, "Library Manager", Dialog.ModalityType.APPLICATION_MODAL);
|
||||
|
||||
setResizable(true);
|
||||
|
||||
Container pane = getContentPane();
|
||||
pane.setLayout(new BorderLayout());
|
||||
|
||||
{
|
||||
categoryStrut1 = Box.createHorizontalStrut(5);
|
||||
categoryStrut2 = Box.createHorizontalStrut(5);
|
||||
categoryStrut3 = Box.createHorizontalStrut(5);
|
||||
|
||||
categoryLabel = new JLabel(_("Category:"));
|
||||
|
||||
categoryChooser = new JComboBox();
|
||||
categoryChooser.setMaximumRowCount(20);
|
||||
categoryChooser.setEnabled(false);
|
||||
|
||||
filterField = new FilterJTextField(_("Filter your search...")) {
|
||||
@Override
|
||||
protected void onFilter(String[] _filters) {
|
||||
filters = _filters;
|
||||
cellEditor.stopCellEditing();
|
||||
contribModel.updateIndexFilter(category, filters);
|
||||
}
|
||||
};
|
||||
|
||||
JPanel panel = new JPanel();
|
||||
panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS));
|
||||
panel.add(categoryStrut1);
|
||||
panel.add(categoryLabel);
|
||||
panel.add(categoryStrut2);
|
||||
panel.add(categoryChooser);
|
||||
panel.add(categoryStrut3);
|
||||
panel.add(filterField);
|
||||
panel.setBorder(new EmptyBorder(7, 7, 7, 7));
|
||||
pane.add(panel, BorderLayout.NORTH);
|
||||
}
|
||||
|
||||
contribTable = new JTable(contribModel);
|
||||
contribTable.setTableHeader(null);
|
||||
contribTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
||||
contribTable.setColumnSelectionAllowed(false);
|
||||
contribTable.setDragEnabled(false);
|
||||
contribTable.setIntercellSpacing(new Dimension(0, 1));
|
||||
contribTable.setShowVerticalLines(false);
|
||||
contribTable.setSelectionBackground(Theme.getColor("status.notice.bgcolor"));
|
||||
{
|
||||
TableColumnModel tcm = contribTable.getColumnModel();
|
||||
TableColumn col = tcm.getColumn(DESCRIPTION_COL);
|
||||
col.setCellRenderer(new ContributedLibraryTableCell());
|
||||
cellEditor = new ContributedLibraryTableCell() {
|
||||
@Override
|
||||
protected void onInstall(ContributedLibrary selectedPlatform) {
|
||||
onInstallPressed(selectedPlatform);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onRemove(ContributedLibrary installedPlatform) {
|
||||
onRemovePressed(installedPlatform);
|
||||
}
|
||||
};
|
||||
col.setCellEditor(cellEditor);
|
||||
col.setResizable(true);
|
||||
}
|
||||
|
||||
{
|
||||
JScrollPane s = new JScrollPane();
|
||||
s.setViewportView(contribTable);
|
||||
s.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
|
||||
s.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
|
||||
pane.add(s, BorderLayout.CENTER);
|
||||
}
|
||||
|
||||
pane.add(Box.createHorizontalStrut(10), BorderLayout.WEST);
|
||||
pane.add(Box.createHorizontalStrut(10), BorderLayout.EAST);
|
||||
|
||||
{
|
||||
progressBar = new ProgressJProgressBar();
|
||||
progressBar.setStringPainted(true);
|
||||
progressBar.setString(" ");
|
||||
progressBar.setVisible(true);
|
||||
|
||||
JButton cancelButton = new JButton(_("Cancel"));
|
||||
cancelButton.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent arg0) {
|
||||
onCancelPressed();
|
||||
}
|
||||
});
|
||||
|
||||
JButton updateButton = new JButton(_("Update list"));
|
||||
updateButton.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent arg0) {
|
||||
onUpdatePressed();
|
||||
}
|
||||
});
|
||||
|
||||
{
|
||||
progressBox = Box.createHorizontalBox();
|
||||
progressBox.add(progressBar);
|
||||
progressBox.add(Box.createHorizontalStrut(5));
|
||||
progressBox.add(cancelButton);
|
||||
|
||||
updateBox = Box.createHorizontalBox();
|
||||
updateBox.add(Box.createHorizontalGlue());
|
||||
updateBox.add(updateButton);
|
||||
|
||||
JPanel progressPanel = new JPanel();
|
||||
progressPanel.setBorder(new EmptyBorder(7, 7, 7, 7));
|
||||
progressPanel.setLayout(new BoxLayout(progressPanel, BoxLayout.Y_AXIS));
|
||||
progressPanel.add(progressBox);
|
||||
progressPanel.add(updateBox);
|
||||
pane.add(progressPanel, BorderLayout.SOUTH);
|
||||
|
||||
setProgressVisible(false);
|
||||
}
|
||||
}
|
||||
|
||||
setMinimumSize(new Dimension(600, 450));
|
||||
}
|
||||
|
||||
private TableModelListener tableModelListener = new TableModelListener() {
|
||||
@Override
|
||||
public void tableChanged(final TableModelEvent arg0) {
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
updateCellsHeight(arg0);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
public void setIndexer(LibrariesIndexer indexer) {
|
||||
contribModel.removeTableModelListener(tableModelListener);
|
||||
categoryChooser.removeActionListener(categoryChooserActionListener);
|
||||
|
||||
contribModel.setIndexer(indexer);
|
||||
|
||||
category = null;
|
||||
categoryChooser.removeAllItems();
|
||||
|
||||
contribModel.addTableModelListener(tableModelListener);
|
||||
categoryChooser.addActionListener(categoryChooserActionListener);
|
||||
|
||||
// Load categories
|
||||
Collection<String> categories = indexer.getIndex().getCategories();
|
||||
categoryChooser.addItem("");
|
||||
for (String s : categories)
|
||||
categoryChooser.addItem(s);
|
||||
|
||||
// Enable categories combo only if there are two or more choices
|
||||
int count = categoryChooser.getItemCount();
|
||||
categoryChooser.setEnabled(count > 1);
|
||||
|
||||
filterField.setEnabled(contribModel.getRowCount() > 0);
|
||||
|
||||
// Create LibrariesInstaller tied with the provided index
|
||||
installer = new LibraryInstaller(indexer) {
|
||||
@Override
|
||||
public void onProgress(Progress progress) {
|
||||
setProgress(progress);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
ActionListener categoryChooserActionListener = new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent arg0) {
|
||||
String selected = (String) categoryChooser.getSelectedItem();
|
||||
if (category == null || !category.equals(selected)) {
|
||||
category = selected;
|
||||
cellEditor.stopCellEditing();
|
||||
contribModel.updateIndexFilter(category, filters);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public void setProgressVisible(boolean visible) {
|
||||
progressBox.setVisible(visible);
|
||||
|
||||
filterField.setEnabled(!visible);
|
||||
categoryChooser.setEnabled(!visible);
|
||||
contribTable.setEnabled(!visible);
|
||||
updateBox.setVisible(!visible);
|
||||
updateBox.setEnabled(!visible);
|
||||
cellEditor.setEnabled(!visible);
|
||||
|
||||
if (visible && contribTable.isEditing()) {
|
||||
TableCellEditor editor = contribTable.getCellEditor();
|
||||
if (editor != null)
|
||||
editor.stopCellEditing();
|
||||
}
|
||||
}
|
||||
|
||||
private void updateCellsHeight(TableModelEvent e) {
|
||||
int first = e.getFirstRow();
|
||||
int last = Math.min(e.getLastRow(), contribTable.getRowCount() - 1);
|
||||
for (int row = first; row <= last; row++) {
|
||||
TableCellRenderer editor = new ContributedLibraryTableCell();
|
||||
Component comp = contribTable.prepareRenderer(editor, row, 0);
|
||||
int height = comp.getPreferredSize().height;
|
||||
contribTable.setRowHeight(row, height);
|
||||
}
|
||||
}
|
||||
|
||||
public void setProgress(Progress progress) {
|
||||
progressBar.setValue(progress);
|
||||
}
|
||||
|
||||
/*
|
||||
* Installer methods follows
|
||||
*/
|
||||
|
||||
private LibraryInstaller installer;
|
||||
private Thread installerThread = null;
|
||||
|
||||
public void onCancelPressed() {
|
||||
if (installerThread != null)
|
||||
installerThread.interrupt();
|
||||
}
|
||||
|
||||
public void onUpdatePressed() {
|
||||
installerThread = new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
setProgressVisible(true);
|
||||
installer.updateIndex();
|
||||
onIndexesUpdated();
|
||||
} catch (Exception e) {
|
||||
// TODO Show ERROR
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
setProgressVisible(false);
|
||||
}
|
||||
}
|
||||
});
|
||||
installerThread.start();
|
||||
}
|
||||
|
||||
public void onInstallPressed(final ContributedLibrary lib) {
|
||||
installerThread = new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
setProgressVisible(true);
|
||||
installer.install(lib);
|
||||
contribModel.updateLibrary(lib);
|
||||
} catch (Exception e) {
|
||||
// TODO Show ERROR
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
setProgressVisible(false);
|
||||
}
|
||||
}
|
||||
});
|
||||
installerThread.start();
|
||||
}
|
||||
|
||||
public void onRemovePressed(final ContributedLibrary lib) {
|
||||
installerThread = new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
setProgressVisible(true);
|
||||
installer.remove(lib);
|
||||
contribModel.updateLibrary(lib);
|
||||
} catch (Exception e) {
|
||||
// TODO Show ERROR
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
setProgressVisible(false);
|
||||
}
|
||||
}
|
||||
});
|
||||
installerThread.start();
|
||||
}
|
||||
|
||||
protected void onIndexesUpdated() throws Exception {
|
||||
// Empty
|
||||
}
|
||||
|
||||
}
|
@ -48,7 +48,6 @@ import javax.swing.JComboBox;
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JProgressBar;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JTable;
|
||||
import javax.swing.ListSelectionModel;
|
||||
@ -65,6 +64,9 @@ import javax.swing.table.TableColumnModel;
|
||||
import cc.arduino.packages.contributions.ContributedPlatform;
|
||||
import cc.arduino.packages.contributions.ContributionInstaller;
|
||||
import cc.arduino.packages.contributions.ContributionsIndexer;
|
||||
import cc.arduino.ui.FilterJTextField;
|
||||
import cc.arduino.ui.ProgressJProgressBar;
|
||||
import cc.arduino.utils.Progress;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class ContributionManagerUI extends JDialog {
|
||||
@ -79,7 +81,7 @@ public class ContributionManagerUI extends JDialog {
|
||||
|
||||
private ContributionIndexTableModel contribModel = new ContributionIndexTableModel();
|
||||
private JTable contribTable;
|
||||
private JProgressBar progressBar;
|
||||
private ProgressJProgressBar progressBar;
|
||||
|
||||
private Box progressBox;
|
||||
private Box updateBox;
|
||||
@ -169,7 +171,7 @@ public class ContributionManagerUI extends JDialog {
|
||||
pane.add(Box.createHorizontalStrut(10), BorderLayout.EAST);
|
||||
|
||||
{
|
||||
progressBar = new JProgressBar();
|
||||
progressBar = new ProgressJProgressBar();
|
||||
progressBar.setStringPainted(true);
|
||||
progressBar.setString(" ");
|
||||
progressBar.setVisible(true);
|
||||
@ -249,13 +251,12 @@ public class ContributionManagerUI extends JDialog {
|
||||
categoryChooser.addItem(s);
|
||||
|
||||
// Create ConstributionInstaller tied with the provided index
|
||||
installer = new ContributionInstaller(indexer);
|
||||
installer.setListener(new ContributionInstaller.Listener() {
|
||||
installer = new ContributionInstaller(indexer) {
|
||||
@Override
|
||||
public void onProgress(double progress, String message) {
|
||||
setProgress((int) progress, message);
|
||||
public void onProgress(Progress progress) {
|
||||
setProgress(progress);
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
ActionListener categoryChooserActionListener = new ActionListener() {
|
||||
@ -298,10 +299,8 @@ public class ContributionManagerUI extends JDialog {
|
||||
}
|
||||
}
|
||||
|
||||
public void setProgress(int progress, String text) {
|
||||
public void setProgress(Progress progress) {
|
||||
progressBar.setValue(progress);
|
||||
if (text != null)
|
||||
progressBar.setString(text);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -26,7 +26,7 @@
|
||||
* invalidate any other reasons why the executable file might be covered by
|
||||
* the GNU General Public License.
|
||||
*/
|
||||
package cc.arduino.packages.contributions.ui;
|
||||
package cc.arduino.ui;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Font;
|
@ -22,15 +22,7 @@
|
||||
|
||||
package processing.app;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.event.*;
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
import cc.arduino.libraries.contributions.LibrariesIndexer;
|
||||
import cc.arduino.libraries.contributions.ui.LibraryManagerUI;
|
||||
import cc.arduino.packages.DiscoveryManager;
|
||||
import cc.arduino.packages.contributions.ui.ContributionManagerUI;
|
||||
import cc.arduino.view.SplashScreenHelper;
|
||||
@ -43,7 +35,6 @@ import processing.app.helpers.filefilters.OnlyFilesWithExtension;
|
||||
import processing.app.javax.swing.filechooser.FileNameExtensionFilter;
|
||||
import processing.app.legacy.PApplet;
|
||||
import processing.app.macosx.ThinkDifferent;
|
||||
import processing.app.legacy.PConstants;
|
||||
import processing.app.packages.LibraryList;
|
||||
import processing.app.packages.UserLibrary;
|
||||
import processing.app.tools.MenuScroller;
|
||||
@ -1020,9 +1011,7 @@ public class Base {
|
||||
}
|
||||
|
||||
public LibraryList getIDELibs() {
|
||||
if (getLibraries() == null)
|
||||
return new LibraryList();
|
||||
LibraryList res = new LibraryList(getLibraries());
|
||||
LibraryList res = new LibraryList(BaseNoGui.librariesIndexer.getInstalledLibraries());
|
||||
res.removeAll(getUserLibs());
|
||||
return res;
|
||||
}
|
||||
@ -1036,16 +1025,13 @@ public class Base {
|
||||
return;
|
||||
importMenu.removeAll();
|
||||
|
||||
JMenuItem addLibraryMenuItem = new JMenuItem(_("Add Library..."));
|
||||
addLibraryMenuItem.addActionListener(new ActionListener() {
|
||||
JMenuItem menu = new JMenuItem(_("Manage libraries..."));
|
||||
menu.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
Base.this.handleAddLibrary();
|
||||
Base.this.onBoardOrPortChange();
|
||||
Base.this.rebuildImportMenu(Editor.importMenu);
|
||||
Base.this.rebuildExamplesMenu(Editor.examplesMenu);
|
||||
openManageLibrariesDialog();
|
||||
}
|
||||
});
|
||||
importMenu.add(addLibraryMenuItem);
|
||||
importMenu.add(menu);
|
||||
importMenu.addSeparator();
|
||||
|
||||
// Split between user supplied libraries and IDE libraries
|
||||
@ -1116,6 +1102,27 @@ public class Base {
|
||||
editor.onBoardOrPortChange();
|
||||
}
|
||||
|
||||
private void openManageLibrariesDialog() {
|
||||
@SuppressWarnings("serial")
|
||||
LibraryManagerUI managerUI = new LibraryManagerUI(activeEditor) {
|
||||
@Override
|
||||
protected void onIndexesUpdated() throws Exception {
|
||||
BaseNoGui.initPackages();
|
||||
rebuildBoardsMenu();
|
||||
onBoardOrPortChange();
|
||||
setIndexer(BaseNoGui.librariesIndexer);
|
||||
}
|
||||
};
|
||||
managerUI.setIndexer(BaseNoGui.librariesIndexer);
|
||||
managerUI.setVisible(true);
|
||||
// Manager dialog is modal, waits here until closed
|
||||
|
||||
//handleAddLibrary();
|
||||
onBoardOrPortChange();
|
||||
rebuildImportMenu(Editor.importMenu);
|
||||
rebuildExamplesMenu(Editor.examplesMenu);
|
||||
}
|
||||
|
||||
private void openInstallBoardDialog() {
|
||||
// Create dialog for contribution manager
|
||||
@SuppressWarnings("serial")
|
||||
@ -1693,8 +1700,9 @@ public class Base {
|
||||
}
|
||||
|
||||
|
||||
// XXX: Remove this method and make librariesIndexer non-static
|
||||
static public LibraryList getLibraries() {
|
||||
return BaseNoGui.getLibraries();
|
||||
return BaseNoGui.librariesIndexer.getInstalledLibraries();
|
||||
}
|
||||
|
||||
|
||||
|
@ -28,6 +28,8 @@
|
||||
*/
|
||||
package cc.arduino.libraries.contributions;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public abstract class LibrariesIndex {
|
||||
@ -49,4 +51,19 @@ public abstract class LibrariesIndex {
|
||||
res += l.toString();
|
||||
return res;
|
||||
}
|
||||
|
||||
private List<String> categories = new ArrayList<String>();
|
||||
|
||||
public List<String> getCategories() {
|
||||
return categories;
|
||||
}
|
||||
|
||||
public void fillCategories() {
|
||||
categories.clear();
|
||||
for (ContributedLibrary lib : getLibraries()) {
|
||||
if (!categories.contains(lib.getCategory()))
|
||||
categories.add(lib.getCategory());
|
||||
}
|
||||
Collections.sort(categories);
|
||||
}
|
||||
}
|
||||
|
@ -38,6 +38,7 @@ import java.util.List;
|
||||
|
||||
import processing.app.BaseNoGui;
|
||||
import processing.app.I18n;
|
||||
import processing.app.helpers.FileUtils;
|
||||
import processing.app.helpers.filefilters.OnlyDirs;
|
||||
import processing.app.packages.LegacyUserLibrary;
|
||||
import processing.app.packages.LibraryList;
|
||||
@ -51,16 +52,23 @@ import com.fasterxml.jackson.module.mrbean.MrBeanModule;
|
||||
public class LibrariesIndexer {
|
||||
|
||||
private LibrariesIndex index;
|
||||
private LibraryList installedLibraries = new LibraryList();
|
||||
private List<File> librariesFolders;
|
||||
private File indexFile;
|
||||
private File stagingFolder;
|
||||
private File sketchbookLibrariesFolder;
|
||||
|
||||
public LibrariesIndexer(File _settingsFolder) {
|
||||
indexFile = new File(_settingsFolder, "library_index.json");
|
||||
public LibrariesIndexer(File preferencesFolder) {
|
||||
indexFile = new File(preferencesFolder, "library_index.json");
|
||||
stagingFolder = new File(preferencesFolder, "staging" + File.separator +
|
||||
"libraries");
|
||||
}
|
||||
|
||||
public void parseIndex() throws JsonParseException, IOException {
|
||||
parseIndex(indexFile);
|
||||
System.out.println(index);
|
||||
|
||||
index.fillCategories();
|
||||
// TODO: resolve libraries inner references
|
||||
}
|
||||
|
||||
@ -75,75 +83,108 @@ public class LibrariesIndexer {
|
||||
index = mapper.readValue(indexIn, LibrariesIndex.class);
|
||||
}
|
||||
|
||||
public LibraryList scanLibraries(List<File> folders) throws IOException {
|
||||
LibraryList res = new LibraryList();
|
||||
for (File folder : folders)
|
||||
res.addOrReplaceAll(scanLibraries(folder));
|
||||
return res;
|
||||
public void setLibrariesFolders(List<File> _librariesFolders)
|
||||
throws IOException {
|
||||
librariesFolders = _librariesFolders;
|
||||
rescanLibraries();
|
||||
}
|
||||
|
||||
private LibraryList scanLibraries(File folder) throws IOException {
|
||||
LibraryList res = new LibraryList();
|
||||
public void rescanLibraries() throws IOException {
|
||||
// Clear all installed flags
|
||||
installedLibraries.clear();
|
||||
for (ContributedLibrary lib : index.getLibraries())
|
||||
lib.setInstalled(false);
|
||||
|
||||
File list[] = folder.listFiles(new OnlyDirs());
|
||||
// Rescan libraries
|
||||
for (File folder : librariesFolders)
|
||||
scanInstalledLibraries(folder);
|
||||
}
|
||||
|
||||
private void scanInstalledLibraries(File folder) {
|
||||
File list[] = folder.listFiles(OnlyDirs.ONLY_DIRS);
|
||||
// if a bad folder or something like that, this might come back null
|
||||
if (list == null)
|
||||
return res;
|
||||
return;
|
||||
|
||||
for (File subfolder : list) {
|
||||
if (!BaseNoGui.isSanitaryName(subfolder.getName())) {
|
||||
String mess = I18n
|
||||
.format(_("The library \"{0}\" cannot be used.\n"
|
||||
+ "Library names must contain only basic letters and numbers.\n"
|
||||
+ "(ASCII only and no spaces, and it cannot start with a number)"),
|
||||
subfolder.getName());
|
||||
String mess = I18n.format(_("The library \"{0}\" cannot be used.\n"
|
||||
+ "Library names must contain only basic letters and numbers.\n"
|
||||
+ "(ASCII only and no spaces, and it cannot start with a number)"),
|
||||
subfolder.getName());
|
||||
BaseNoGui.showMessage(_("Ignoring bad library name"), mess);
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
ContributedLibrary lib = scanLibrary(subfolder);
|
||||
|
||||
// (also replace previously found libs with the same name)
|
||||
if (lib != null)
|
||||
res.addOrReplace(lib);
|
||||
scanLibrary(subfolder);
|
||||
} catch (IOException e) {
|
||||
System.out.println(I18n.format(_("Invalid library found in {0}: {1}"),
|
||||
subfolder, e.getMessage()));
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
private ContributedLibrary scanLibrary(File subfolder) throws IOException {
|
||||
// A library is considered non-Legacy if it contains
|
||||
// a file called "library.properties"
|
||||
File check = new File(subfolder, "library.properties");
|
||||
if (!check.exists() || !check.isFile())
|
||||
return LegacyUserLibrary.create(subfolder);
|
||||
private void scanLibrary(File folder) throws IOException {
|
||||
boolean readOnly = !FileUtils
|
||||
.isSubDirectory(sketchbookLibrariesFolder, folder);
|
||||
|
||||
ContributedLibrary lib = UserLibrary.create(subfolder);
|
||||
// A library is considered "legacy" if it doesn't contains
|
||||
// a file called "library.properties"
|
||||
File check = new File(folder, "library.properties");
|
||||
if (!check.exists() || !check.isFile()) {
|
||||
|
||||
// Create a legacy library and exit
|
||||
LegacyUserLibrary lib = LegacyUserLibrary.create(folder);
|
||||
lib.setReadOnly(readOnly);
|
||||
installedLibraries.addOrReplace(lib);
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a regular library
|
||||
UserLibrary lib = UserLibrary.create(folder);
|
||||
lib.setReadOnly(readOnly);
|
||||
installedLibraries.addOrReplace(lib);
|
||||
|
||||
// Check if we can find the same library in the index
|
||||
// String libName = subfolder.getName(); // XXX: lib.getName()?
|
||||
// ContributedLibrary foundLib = index.find(libName, lib.getVersion());
|
||||
// if (foundLib != null) {
|
||||
// foundLib.setInstalled(true);
|
||||
// foundLib.setInstalledFolder(subfolder);
|
||||
// return foundLib;
|
||||
// }
|
||||
|
||||
return lib;
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws JsonParseException, IOException {
|
||||
LibrariesIndexer indexer = new LibrariesIndexer(new File(
|
||||
"/home/megabug/.arduino15"));
|
||||
indexer.parseIndex();
|
||||
LibraryList libs = indexer.scanLibraries(new File(
|
||||
"/home/megabug/sketchbook/libraries"));
|
||||
for (ContributedLibrary lib : libs) {
|
||||
System.out.println(lib);
|
||||
// and mark it as installed
|
||||
String libName = folder.getName(); // XXX: lib.getName()?
|
||||
ContributedLibrary foundLib = index.find(libName, lib.getVersion());
|
||||
if (foundLib != null) {
|
||||
foundLib.setInstalled(true);
|
||||
foundLib.setInstalledFolder(folder);
|
||||
foundLib.setReadOnly(readOnly);
|
||||
}
|
||||
}
|
||||
|
||||
public LibrariesIndex getIndex() {
|
||||
return index;
|
||||
}
|
||||
|
||||
public LibraryList getInstalledLibraries() {
|
||||
return installedLibraries;
|
||||
}
|
||||
|
||||
public File getStagingFolder() {
|
||||
return stagingFolder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the sketchbook library folder. <br />
|
||||
* New libraries will be installed here. <br />
|
||||
* Libraries not found on this folder will be marked as read-only.
|
||||
*
|
||||
* @param folder
|
||||
*/
|
||||
public void setSketchbookLibrariesFolder(File folder) {
|
||||
this.sketchbookLibrariesFolder = folder;
|
||||
}
|
||||
|
||||
public File getSketchbookLibrariesFolder() {
|
||||
return sketchbookLibrariesFolder;
|
||||
}
|
||||
|
||||
public File getIndexFile() {
|
||||
return indexFile;
|
||||
}
|
||||
}
|
||||
|
@ -42,42 +42,15 @@ import java.util.Observer;
|
||||
import processing.app.helpers.FileUtils;
|
||||
import cc.arduino.utils.ArchiveExtractor;
|
||||
import cc.arduino.utils.FileHash;
|
||||
import cc.arduino.utils.MultiStepProgress;
|
||||
import cc.arduino.utils.Progress;
|
||||
import cc.arduino.utils.network.FileDownloader;
|
||||
|
||||
public class ContributionInstaller {
|
||||
|
||||
/**
|
||||
* Listener for installation progress.
|
||||
*/
|
||||
public static interface Listener {
|
||||
/**
|
||||
* Receive the latest progress update.
|
||||
*
|
||||
* @param progress
|
||||
* Actual progress in the range 0...100
|
||||
* @param message
|
||||
* A verbose description message of the actual operation
|
||||
*/
|
||||
void onProgress(double progress, String message);
|
||||
}
|
||||
|
||||
private Listener listener = null;
|
||||
|
||||
private File stagingFolder;
|
||||
private ContributionsIndexer indexer;
|
||||
|
||||
private double progress;
|
||||
private double progressStepsDelta;
|
||||
|
||||
public void setListener(Listener listener) {
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
private void updateProgress(double progress, String message) {
|
||||
if (listener != null)
|
||||
listener.onProgress(progress, message);
|
||||
}
|
||||
|
||||
public ContributionInstaller(ContributionsIndexer contributionsIndexer) {
|
||||
stagingFolder = contributionsIndexer.getStagingFolder();
|
||||
indexer = contributionsIndexer;
|
||||
@ -102,19 +75,18 @@ public class ContributionInstaller {
|
||||
}
|
||||
|
||||
// Calculate progress increases
|
||||
progress = 0.0;
|
||||
progressStepsDelta = 100.0 / (tools.size() + 1) / 2.0;
|
||||
MultiStepProgress progress = new MultiStepProgress((tools.size() + 1) * 2);
|
||||
|
||||
// Download all
|
||||
try {
|
||||
// Download platform
|
||||
download(platform, _("Downloading boards definitions."));
|
||||
download(platform, progress, _("Downloading boards definitions."));
|
||||
|
||||
// Download tools
|
||||
int i = 1;
|
||||
for (ContributedTool tool : tools) {
|
||||
String msg = format(_("Downloading tools ({0}/{1})."), i, tools.size());
|
||||
download(tool.getDownloadableContribution(), msg);
|
||||
download(tool.getDownloadableContribution(), progress, msg);
|
||||
i++;
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
@ -133,8 +105,9 @@ public class ContributionInstaller {
|
||||
File toolsFolder = new File(packageFolder, "tools");
|
||||
int i = 1;
|
||||
for (ContributedTool tool : platform.getResolvedTools()) {
|
||||
String msg = format(_("Installing tools ({0}/{1})..."), i, tools.size());
|
||||
updateProgress(progress, msg);
|
||||
progress.setStatus(format(_("Installing tools ({0}/{1})..."), i,
|
||||
tools.size()));
|
||||
onProgress(progress);
|
||||
i++;
|
||||
DownloadableContribution toolContrib = tool.getDownloadableContribution();
|
||||
File destFolder = new File(toolsFolder, tool.getName() + File.separator +
|
||||
@ -144,11 +117,12 @@ public class ContributionInstaller {
|
||||
ArchiveExtractor.extract(toolContrib.getDownloadedFile(), destFolder, 1);
|
||||
toolContrib.setInstalled(true);
|
||||
toolContrib.setInstalledFolder(destFolder);
|
||||
progress += progressStepsDelta;
|
||||
progress.stepDone();
|
||||
}
|
||||
|
||||
// Unpack platform on the correct location
|
||||
updateProgress(progress, _("Installing boards..."));
|
||||
progress.setStatus(_("Installing boards..."));
|
||||
onProgress(progress);
|
||||
File platformFolder = new File(packageFolder, "hardware" + File.separator +
|
||||
platform.getArchitecture());
|
||||
File destFolder = new File(platformFolder, platform.getVersion());
|
||||
@ -156,13 +130,15 @@ public class ContributionInstaller {
|
||||
ArchiveExtractor.extract(platform.getDownloadedFile(), destFolder, 1);
|
||||
platform.setInstalled(true);
|
||||
platform.setInstalledFolder(destFolder);
|
||||
progress += progressStepsDelta;
|
||||
progress.stepDone();
|
||||
|
||||
updateProgress(100.0, _("Installation completed!"));
|
||||
progress.setStatus(_("Installation completed!"));
|
||||
onProgress(progress);
|
||||
}
|
||||
|
||||
public File download(DownloadableContribution contribution,
|
||||
final String statusText) throws Exception {
|
||||
final MultiStepProgress progress, final String statusText)
|
||||
throws Exception {
|
||||
URL url = new URL(contribution.getUrl());
|
||||
String path = url.getPath();
|
||||
String fileName = path.substring(path.lastIndexOf('/') + 1);
|
||||
@ -186,18 +162,20 @@ public class ContributionInstaller {
|
||||
long total = me.getInitialSize() + me.getDownloadSize() / 1000;
|
||||
msg = format(_("Downloaded {0}kb of {1}kb."), downloaded, total);
|
||||
}
|
||||
updateProgress((int) progress + progressStepsDelta *
|
||||
me.getProgress() / 100.0, statusText + " " + msg);
|
||||
progress.setStatus(statusText + " " + msg);
|
||||
progress.setProgress(me.getProgress());
|
||||
onProgress(progress);
|
||||
}
|
||||
});
|
||||
downloader.download();
|
||||
if (!downloader.isCompleted())
|
||||
throw new Exception("Error dowloading " + url, downloader.getError());
|
||||
}
|
||||
progress += progressStepsDelta;
|
||||
progress.stepDone();
|
||||
|
||||
// Test checksum
|
||||
updateProgress(progress, _("Verifying archive integrity..."));
|
||||
progress.setStatus(_("Verifying archive integrity..."));
|
||||
onProgress(progress);
|
||||
String checksum = contribution.getChecksum();
|
||||
String algo = checksum.split(":")[0];
|
||||
if (!FileHash.hash(outputFile, algo).equals(checksum))
|
||||
@ -236,8 +214,8 @@ public class ContributionInstaller {
|
||||
}
|
||||
|
||||
public void updateIndex() throws Exception {
|
||||
final MultiStepProgress progress = new MultiStepProgress(2);
|
||||
final String statusText = _("Downloading platforms index...");
|
||||
updateProgress(0, statusText);
|
||||
|
||||
URL url = new URL("http://arduino.cc/package_index.json");
|
||||
File tmpFile = File.createTempFile("package_index", ".json");
|
||||
@ -252,13 +230,15 @@ public class ContributionInstaller {
|
||||
long total = me.getInitialSize() + me.getDownloadSize() / 1000;
|
||||
msg = format(_("Downloaded {0}kb of {1}kb."), downloaded, total);
|
||||
}
|
||||
updateProgress((int) progress + progressStepsDelta * me.getProgress() /
|
||||
100.0, statusText + " " + msg);
|
||||
progress.setStatus(statusText + " " + msg);
|
||||
progress.setProgress(me.getProgress());
|
||||
onProgress(progress);
|
||||
}
|
||||
});
|
||||
downloader.download();
|
||||
if (!downloader.isCompleted())
|
||||
throw new Exception("Error dowloading " + url, downloader.getError());
|
||||
progress.stepDone();
|
||||
|
||||
// TODO: Check downloaded index
|
||||
|
||||
@ -269,4 +249,8 @@ public class ContributionInstaller {
|
||||
if (!tmpFile.renameTo(outputFile))
|
||||
throw new Exception("An error occurred while updating platforms index!");
|
||||
}
|
||||
|
||||
protected void onProgress(Progress progress) {
|
||||
// Empty
|
||||
}
|
||||
}
|
||||
|
@ -32,10 +32,21 @@ import java.util.Comparator;
|
||||
|
||||
public class VersionComparator implements Comparator<String> {
|
||||
|
||||
// An handy pre-instatiated object
|
||||
public static final VersionComparator VERSION_COMPARATOR = new VersionComparator();
|
||||
|
||||
@Override
|
||||
public int compare(String o1, String o2) {
|
||||
// TODO: do a proper version compare
|
||||
return o1.compareTo(o2);
|
||||
public int compare(String a, String b) {
|
||||
// null is always less than any other value
|
||||
if (a == null && b == null)
|
||||
return 0;
|
||||
if (a == null)
|
||||
return -1;
|
||||
if (b == null)
|
||||
return 1;
|
||||
|
||||
// TODO: do a proper version compare. Look also http://semver.org/
|
||||
return a.compareTo(b);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -64,6 +64,7 @@ public class BaseNoGui {
|
||||
// maps library name to their library folder
|
||||
static private LibraryList libraries;
|
||||
|
||||
// XXX: Remove this field
|
||||
static private List<File> librariesFolders;
|
||||
|
||||
static UserNotifier notifier = new BasicUserNotifier();
|
||||
@ -410,9 +411,8 @@ public class BaseNoGui {
|
||||
}
|
||||
|
||||
static public LibraryList getUserLibs() {
|
||||
if (libraries == null)
|
||||
return new LibraryList();
|
||||
return libraries.filterLibrariesInSubfolder(getSketchbookFolder());
|
||||
LibraryList libs = BaseNoGui.librariesIndexer.getInstalledLibraries();
|
||||
return libs.filterLibrariesInSubfolder(getSketchbookFolder());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -727,12 +727,14 @@ public class BaseNoGui {
|
||||
if (referencedPlatform != null) {
|
||||
File referencedPlatformFolder = referencedPlatform.getFolder();
|
||||
// Add libraries folder for the referenced platform
|
||||
librariesFolders.add(new File(referencedPlatformFolder, "libraries"));
|
||||
File folder = new File(referencedPlatformFolder, "libraries");
|
||||
librariesFolders.add(folder);
|
||||
}
|
||||
}
|
||||
File platformFolder = targetPlatform.getFolder();
|
||||
// Add libraries folder for the selected platform
|
||||
librariesFolders.add(new File(platformFolder, "libraries"));
|
||||
File folder = new File(platformFolder, "libraries");
|
||||
librariesFolders.add(folder);
|
||||
}
|
||||
|
||||
// Add libraries folder for the sketchbook
|
||||
@ -742,7 +744,9 @@ public class BaseNoGui {
|
||||
// Libraries located in the latest folders on the list can override
|
||||
// other libraries with the same name.
|
||||
try {
|
||||
scanAndUpdateLibraries(librariesFolders);
|
||||
BaseNoGui.librariesIndexer.setSketchbookLibrariesFolder(getSketchbookLibrariesFolder());
|
||||
BaseNoGui.librariesIndexer.setLibrariesFolders(librariesFolders);
|
||||
BaseNoGui.librariesIndexer.rescanLibraries();
|
||||
} catch (IOException e) {
|
||||
showWarning(_("Error"), _("Error loading libraries"), e);
|
||||
}
|
||||
@ -759,7 +763,7 @@ public class BaseNoGui {
|
||||
static public void populateImportToLibraryTable() {
|
||||
// Populate importToLibraryTable
|
||||
importToLibraryTable = new HashMap<String, UserLibrary>();
|
||||
for (UserLibrary lib : getLibraries()) {
|
||||
for (UserLibrary lib : librariesIndexer.getInstalledLibraries()) {
|
||||
try {
|
||||
String headers[] = headerListFromIncludePath(lib.getSrcFolder());
|
||||
for (String header : headers) {
|
||||
@ -968,14 +972,6 @@ public class BaseNoGui {
|
||||
}
|
||||
}
|
||||
|
||||
static public void scanAndUpdateLibraries(List<File> folders) throws IOException {
|
||||
libraries = scanLibraries(folders);
|
||||
}
|
||||
|
||||
static public LibraryList scanLibraries(List<File> folders) throws IOException {
|
||||
return librariesIndexer.scanLibraries(folders);
|
||||
}
|
||||
|
||||
static public void selectBoard(TargetBoard targetBoard) {
|
||||
TargetPlatform targetPlatform = targetBoard.getContainerPlatform();
|
||||
TargetPackage targetPackage = targetPlatform.getContainerPackage();
|
||||
|
@ -120,9 +120,7 @@ public class LegacyUserLibrary extends UserLibrary {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
String res = "LegacyLibrary:";
|
||||
res += " (name=" + name + ")";
|
||||
return res;
|
||||
return "LegacyLibrary:" + name + "\n";
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -30,7 +30,6 @@ package processing.app.packages;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
import processing.app.helpers.FileUtils;
|
||||
@ -54,15 +53,14 @@ public class LibraryList extends ArrayList<UserLibrary> {
|
||||
}
|
||||
|
||||
public void addOrReplace(UserLibrary lib) {
|
||||
UserLibrary l = getByName(lib.getName());
|
||||
if (l != null)
|
||||
remove(l);
|
||||
remove(lib);
|
||||
add(lib);
|
||||
}
|
||||
|
||||
public void addOrReplaceAll(Collection<? extends UserLibrary> c) {
|
||||
for (UserLibrary l : c)
|
||||
addOrReplace(l);
|
||||
public void remove(UserLibrary lib) {
|
||||
UserLibrary l = getByName(lib.getName());
|
||||
if (l != null)
|
||||
super.remove(l);
|
||||
}
|
||||
|
||||
public void sort() {
|
||||
|
@ -257,15 +257,14 @@ public class UserLibrary extends ContributedLibrary {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
String res = "Library:";
|
||||
res += " (name=" + name + ")";
|
||||
res += " (version=" + version + ")";
|
||||
res += " (author=" + author + ")";
|
||||
res += " (maintainer=" + maintainer + ")";
|
||||
res += " (sentence=" + sentence + ")";
|
||||
res += " (paragraph=" + paragraph + ")";
|
||||
res += " (url=" + website + ")";
|
||||
res += " (architectures=" + architectures + ")";
|
||||
String res = "Library: " + name + "\n";
|
||||
res += " (version=" + version + ")\n";
|
||||
res += " (author=" + author + ")\n";
|
||||
res += " (maintainer=" + maintainer + ")\n";
|
||||
res += " (sentence=" + sentence + ")\n";
|
||||
res += " (paragraph=" + paragraph + ")\n";
|
||||
res += " (url=" + website + ")\n";
|
||||
res += " (architectures=" + architectures + ")\n";
|
||||
return res;
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user