diff --git a/app/src/cc/arduino/packages/contributions/ui/ContributedPlatformTableCellRenderer.java b/app/src/cc/arduino/packages/contributions/ui/ContributedPlatformTableCellRenderer.java
new file mode 100644
index 000000000..25b566826
--- /dev/null
+++ b/app/src/cc/arduino/packages/contributions/ui/ContributedPlatformTableCellRenderer.java
@@ -0,0 +1,121 @@
+/*
+ * 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.packages.contributions.ui;
+
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Insets;
+
+import javax.swing.BoxLayout;
+import javax.swing.JPanel;
+import javax.swing.JTable;
+import javax.swing.JTextPane;
+import javax.swing.border.EmptyBorder;
+import javax.swing.table.TableCellRenderer;
+import javax.swing.text.Document;
+import javax.swing.text.html.HTMLDocument;
+import javax.swing.text.html.StyleSheet;
+
+import cc.arduino.packages.contributions.ContributedBoard;
+import cc.arduino.packages.contributions.ContributedPlatform;
+
+@SuppressWarnings("serial")
+public class ContributedPlatformTableCellRenderer extends JPanel implements
+ TableCellRenderer {
+
+ private JTextPane description;
+
+ public ContributedPlatformTableCellRenderer() {
+ super();
+
+ // Align contents to the left
+ setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
+ setAlignmentX(Component.LEFT_ALIGNMENT);
+
+ description = new JTextPane();
+ description.setInheritsPopupMenu(true);
+ Insets margin = description.getMargin();
+ margin.bottom = 0;
+ description.setMargin(margin);
+ description.setContentType("text/html");
+ setTextStyle(description);
+ description.setOpaque(false);
+ description.setBorder(new EmptyBorder(4, 7, 7, 7));
+ description.setHighlighter(null);
+ add(description);
+ }
+
+ static void setTextStyle(JTextPane textPane) {
+ Document doc = textPane.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; }");
+ }
+ }
+
+ public Component getTableCellRendererComponent(JTable table, Object value,
+ boolean isSelected,
+ boolean hasFocus, int row,
+ int column) {
+ ContributedPlatform contrib = (ContributedPlatform) value;
+
+ String descriptionText = "
" + contrib.getName() + "";
+ String authorList = "arduino"; // contrib.getAuthorList();
+ if (authorList != null && !authorList.isEmpty()) {
+ descriptionText += " by Arduino";
+ }
+ descriptionText += "
";
+
+ descriptionText += "Boards contributed in this package:
";
+ for (ContributedBoard board : contrib.getBoards())
+ descriptionText += "- " + board.getName() + "
";
+ // descriptionText += "
Available version: 1.5.5
";
+ // descriptionText += "Installed version: 1.5.4
";
+ descriptionText += "";
+ description.setText(descriptionText);
+ description.setBackground(Color.WHITE);
+ description.setVisible(true);
+
+ int h = getPreferredSize().height;
+ if (table.getRowHeight(row) != h)
+ table.setRowHeight(row, h);
+
+ if (isSelected) {
+ setBackground(table.getSelectionBackground());
+ setForeground(table.getSelectionForeground());
+ } else {
+ setBackground(table.getBackground());
+ setForeground(table.getForeground());
+ }
+ return this;
+ }
+}
diff --git a/app/src/cc/arduino/packages/contributions/ui/ContributionIndexTableModel.java b/app/src/cc/arduino/packages/contributions/ui/ContributionIndexTableModel.java
new file mode 100644
index 000000000..0999480a5
--- /dev/null
+++ b/app/src/cc/arduino/packages/contributions/ui/ContributionIndexTableModel.java
@@ -0,0 +1,194 @@
+/*
+ * 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.packages.contributions.ui;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.swing.table.AbstractTableModel;
+
+import cc.arduino.packages.contributions.ContributedPackage;
+import cc.arduino.packages.contributions.ContributedPlatform;
+import cc.arduino.packages.contributions.ContributionsIndex;
+
+@SuppressWarnings("serial")
+public class ContributionIndexTableModel extends AbstractTableModel {
+
+ public final static int DESCRIPTION_COL = 0;
+ public final static int VERSION_COL = 1;
+ public final static int INSTALLED_COL = 2;
+
+ public static class ContributedPlatformReleases {
+ public ContributedPackage packager;
+ public String arch;
+ public List releases = new ArrayList();
+ public List versions = new ArrayList();
+ public ContributedPlatform selected = null;
+
+ public ContributedPlatformReleases(ContributedPlatform platform) {
+ packager = platform.getParentPackage();
+ arch = platform.getArchitecture();
+ add(platform);
+ }
+
+ public boolean shouldContain(ContributedPlatform platform) {
+ if (platform.getParentPackage() != packager)
+ return false;
+ if (!platform.getArchitecture().equals(arch))
+ return false;
+ return true;
+ }
+
+ public void add(ContributedPlatform platform) {
+ releases.add(platform);
+ versions.add(platform.getVersion());
+ selected = getLatest();
+ }
+
+ public ContributedPlatform getInstalled() {
+ for (ContributedPlatform plat : releases)
+ if (plat.isInstalled())
+ return plat;
+ return null;
+ }
+
+ public ContributedPlatform getLatest() {
+ ContributedPlatform latest = null;
+ for (ContributedPlatform plat : releases) {
+ if (latest == null)
+ latest = plat;
+ if (plat.getVersion().compareTo(latest.getVersion()) > 0)
+ latest = plat;
+ }
+ return latest;
+ }
+
+ public ContributedPlatform getSelected() {
+ return selected;
+ }
+
+ public void selectVersion(String version) {
+ for (ContributedPlatform plat : releases) {
+ if (plat.getVersion().equals(version)) {
+ selected = plat;
+ return;
+ }
+ }
+ }
+ }
+
+ private List contributions = new ArrayList();
+
+ private String[] m_colNames = { "Description", "Available", "Installed" };
+
+ private Class>[] m_colTypes = { ContributedPlatform.class, Object[].class,
+ String.class };
+
+ public void updateIndex(ContributionsIndex index) {
+ contributions.clear();
+ for (ContributedPackage pack : index.getPackages()) {
+ for (ContributedPlatform platform : pack.getPlatforms()) {
+ addContribution(platform);
+ }
+ }
+ }
+
+ private void addContribution(ContributedPlatform platform) {
+ for (ContributedPlatformReleases contribution : contributions) {
+ if (!contribution.shouldContain(platform))
+ continue;
+ contribution.add(platform);
+ return;
+ }
+
+ contributions.add(new ContributedPlatformReleases(platform));
+ }
+
+ @Override
+ public int getColumnCount() {
+ return m_colNames.length;
+ }
+
+ @Override
+ public int getRowCount() {
+ return contributions.size();
+ }
+
+ @Override
+ public String getColumnName(int column) {
+ return m_colNames[column];
+ }
+
+ @Override
+ public Class> getColumnClass(int col) {
+ return m_colTypes[col];
+ }
+
+ @Override
+ public void setValueAt(Object value, int row, int col) {
+ if (col == VERSION_COL) {
+ contributions.get(row).selectVersion((String) value);
+ fireTableCellUpdated(row, col);
+ }
+ }
+
+ @Override
+ public Object getValueAt(int row, int col) {
+ ContributedPlatformReleases contribution = contributions.get(row);
+ ContributedPlatform installed = contribution.getInstalled();
+ if (col == DESCRIPTION_COL) {
+ return contribution.getSelected();
+ }
+ if (col == VERSION_COL) {
+ return contribution.getSelected().getVersion();
+ }
+ if (col == INSTALLED_COL) {
+ return installed == null ? "-" : installed.getVersion();
+ }
+ return null;
+ }
+
+ @Override
+ public boolean isCellEditable(int row, int col) {
+ return col == VERSION_COL || col == INSTALLED_COL;
+ }
+
+ public List getReleasesVersions(int row) {
+ return contributions.get(row).versions;
+ }
+
+ public ContributedPlatformReleases getReleases(int row) {
+ return contributions.get(row);
+ }
+
+ public ContributedPlatform getSelectedRelease(int row) {
+ return contributions.get(row).getSelected();
+ }
+
+}
diff --git a/app/src/cc/arduino/packages/contributions/ui/JContributionManagerDialog.java b/app/src/cc/arduino/packages/contributions/ui/JContributionManagerDialog.java
new file mode 100644
index 000000000..370277a42
--- /dev/null
+++ b/app/src/cc/arduino/packages/contributions/ui/JContributionManagerDialog.java
@@ -0,0 +1,318 @@
+/*
+ * 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.packages.contributions.ui;
+
+import static cc.arduino.packages.contributions.ui.ContributionIndexTableModel.DESCRIPTION_COL;
+import static cc.arduino.packages.contributions.ui.ContributionIndexTableModel.INSTALLED_COL;
+import static cc.arduino.packages.contributions.ui.ContributionIndexTableModel.VERSION_COL;
+import static processing.app.I18n._;
+
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Container;
+import java.awt.Dialog;
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.Frame;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.FocusEvent;
+import java.awt.event.FocusListener;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+
+import javax.swing.Box;
+import javax.swing.BoxLayout;
+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.JTextField;
+import javax.swing.ListSelectionModel;
+import javax.swing.ScrollPaneConstants;
+import javax.swing.UIManager;
+import javax.swing.border.EmptyBorder;
+import javax.swing.event.DocumentEvent;
+import javax.swing.event.DocumentListener;
+import javax.swing.table.TableColumn;
+import javax.swing.table.TableColumnModel;
+
+import cc.arduino.packages.contributions.ContributedPlatform;
+import cc.arduino.packages.contributions.ContributionsIndex;
+
+@SuppressWarnings("serial")
+public class JContributionManagerDialog extends JDialog {
+
+ private FilterField filterField;
+ private JScrollPane scrollPane;
+ private StatusPanel status;
+
+ private JContributionManagerDialogListener listener = null;
+
+ private String category;
+ private JLabel categoryLabel;
+ private JComboBox categoryChooser;
+ private Component categoryStrut1;
+ private Component categoryStrut2;
+ private Component categoryStrut3;
+
+ private ContributionIndexTableModel contribModel = new ContributionIndexTableModel();
+ private JTable contribTable;
+
+ public JContributionManagerDialog(Frame parent) {
+ super(parent, "Boards 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.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent arg0) {
+ notifyCategoryChange();
+ }
+ });
+
+ setCategories(new ArrayList());
+
+ filterField = new FilterField();
+
+ JPanel filterPanel = new JPanel();
+ filterPanel.setLayout(new BoxLayout(filterPanel, BoxLayout.X_AXIS));
+ pane.add(filterPanel, BorderLayout.NORTH);
+ filterPanel.add(categoryStrut1);
+ filterPanel.add(categoryLabel);
+ filterPanel.add(categoryStrut2);
+ filterPanel.add(categoryChooser);
+ filterPanel.add(categoryStrut3);
+ filterPanel.add(filterField);
+ filterPanel.setBorder(new EmptyBorder(7, 7, 7, 7));
+
+ contribTable = new JTable(contribModel);
+ // contribTable.setTableHeader(null);
+ // contribTable.setRowSelectionAllowed(false);
+ contribTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
+ contribTable.setColumnSelectionAllowed(false);
+ contribTable.setDragEnabled(false);
+ contribTable.setIntercellSpacing(new Dimension(0, 1));
+ contribTable.setShowVerticalLines(false);
+ contribTable.getTableHeader().setEnabled(false);
+ // contribTable.addMouseListener(new MouseAdapter() {
+ // @Override
+ // public void mousePressed(MouseEvent e) {
+ // if (listener == null)
+ // return;
+ // Point point = e.getPoint();
+ // int row = contribTable.rowAtPoint(point);
+ // int col = contribTable.columnAtPoint(point);
+ // }
+ // });
+ TableColumnModel tcm = contribTable.getColumnModel();
+ TableColumn descriptionCol = tcm.getColumn(DESCRIPTION_COL);
+ TableColumn versionCol = tcm.getColumn(VERSION_COL);
+ TableColumn installedCol = tcm.getColumn(INSTALLED_COL);
+
+ descriptionCol.setCellRenderer(new ContributedPlatformTableCellRenderer());
+ descriptionCol.setResizable(true);
+
+ {
+ versionCol.setCellRenderer(new VersionSelectorTableCellRenderer());
+ VersionSelectorTableCellEditor editor = new VersionSelectorTableCellEditor();
+ editor.setListener(new VersionSelectorTableCellEditor.Listener() {
+ @Override
+ public void onInstallEvent(int row) {
+ if (listener == null)
+ return;
+ ContributedPlatform selected = contribModel.getSelectedRelease(row);
+ listener.onInstall(selected);
+ }
+ });
+ versionCol.setCellEditor(editor);
+ versionCol.setResizable(false);
+ versionCol.setWidth(140);
+ }
+
+ {
+ installedCol.setCellRenderer(new VersionInstalledTableCellRenderer());
+ VersionInstalledTableCellEditor editor = new VersionInstalledTableCellEditor();
+ editor.setListener(new VersionInstalledTableCellEditor.Listener() {
+ @Override
+ public void onRemoveEvent(int row) {
+ if (listener == null)
+ return;
+ ContributedPlatform installed = contribModel.getReleases(row)
+ .getInstalled();
+ listener.onRemove(installed);
+ }
+ });
+ installedCol.setCellEditor(editor);
+ installedCol.setResizable(false);
+ installedCol.setMaxWidth(70);
+ }
+
+ scrollPane = new JScrollPane();
+ scrollPane.setPreferredSize(new Dimension(300, 300));
+ scrollPane.setViewportView(contribTable);
+ scrollPane
+ .setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
+ scrollPane
+ .setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
+ pane.add(scrollPane, BorderLayout.CENTER);
+
+ pane.add(Box.createHorizontalStrut(10), BorderLayout.WEST);
+ pane.add(Box.createHorizontalStrut(10), BorderLayout.EAST);
+
+ status = new StatusPanel();
+ status.setBorder(new EmptyBorder(7, 7, 7, 7));
+ pane.add(status, BorderLayout.SOUTH);
+
+ setMinimumSize(new Dimension(450, 400));
+ }
+
+ public void setListener(JContributionManagerDialogListener listener) {
+ this.listener = listener;
+ }
+
+ public void setCategories(Collection categories) {
+ category = null;
+ categoryChooser.removeAllItems();
+ for (String s : categories)
+ categoryChooser.addItem(s);
+
+ // Disable if only one possible choice
+ boolean single = categories.size() == 1;
+ categoryChooser.setEnabled(!single);
+
+ // Show if there is at lease one possible choice
+ boolean show = !categories.isEmpty();
+ categoryStrut1.setVisible(show);
+ categoryLabel.setVisible(show);
+ categoryStrut2.setVisible(show);
+ categoryChooser.setVisible(show);
+ categoryStrut3.setVisible(show);
+ }
+
+ private synchronized void notifyCategoryChange() {
+ if (listener == null)
+ return;
+ String selected = (String) categoryChooser.getSelectedItem();
+ if (category == null || !category.equals(selected)) {
+ category = selected;
+ listener.onCategoryChange(category);
+ }
+ }
+
+ class FilterField extends JTextField {
+ final static String filterHint = "Filter your search...";
+ boolean showingHint;
+ List filters;
+
+ public FilterField() {
+ super(filterHint);
+
+ showingHint = true;
+ filters = new ArrayList();
+ updateStyle();
+
+ addFocusListener(new FocusListener() {
+ public void focusLost(FocusEvent focusEvent) {
+ if (filterField.getText().isEmpty()) {
+ showingHint = true;
+ }
+ updateStyle();
+ }
+
+ public void focusGained(FocusEvent focusEvent) {
+ if (showingHint) {
+ showingHint = false;
+ filterField.setText("");
+ }
+ updateStyle();
+ }
+ });
+
+ getDocument().addDocumentListener(new DocumentListener() {
+ public void removeUpdate(DocumentEvent e) {
+ applyFilter();
+ }
+
+ public void insertUpdate(DocumentEvent e) {
+ applyFilter();
+ }
+
+ public void changedUpdate(DocumentEvent e) {
+ applyFilter();
+ }
+ });
+ }
+
+ public void applyFilter() {
+ String filter = filterField.getFilterText();
+ filter = filter.toLowerCase();
+
+ // Replace anything but 0-9, a-z, or : with a space
+ filter = filter.replaceAll("[^\\x30-\\x39^\\x61-\\x7a^\\x3a]", " ");
+ filters = Arrays.asList(filter.split(" "));
+ // filterLibraries(category, filters);
+ }
+
+ public String getFilterText() {
+ return showingHint ? "" : getText();
+ }
+
+ public void updateStyle() {
+ if (showingHint) {
+ setText(filterHint);
+ setForeground(Color.gray);
+ setFont(getFont().deriveFont(Font.ITALIC));
+ } else {
+ setForeground(UIManager.getColor("TextField.foreground"));
+ setFont(getFont().deriveFont(Font.PLAIN));
+ }
+ }
+ }
+
+ public void addContributions(ContributionsIndex index) {
+ contribModel.updateIndex(index);
+ }
+
+}
diff --git a/app/src/cc/arduino/packages/contributions/ui/JContributionManagerDialogListener.java b/app/src/cc/arduino/packages/contributions/ui/JContributionManagerDialogListener.java
new file mode 100644
index 000000000..2a91aa304
--- /dev/null
+++ b/app/src/cc/arduino/packages/contributions/ui/JContributionManagerDialogListener.java
@@ -0,0 +1,41 @@
+/*
+ * 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.packages.contributions.ui;
+
+import cc.arduino.packages.contributions.ContributedPlatform;
+
+public interface JContributionManagerDialogListener {
+
+ void onCategoryChange(String category);
+
+ void onInstall(ContributedPlatform selected);
+
+ void onRemove(ContributedPlatform selected);
+
+}
\ No newline at end of file
diff --git a/app/src/cc/arduino/packages/contributions/ui/VersionInstalledTableCellEditor.java b/app/src/cc/arduino/packages/contributions/ui/VersionInstalledTableCellEditor.java
new file mode 100644
index 000000000..6ac6f9cbd
--- /dev/null
+++ b/app/src/cc/arduino/packages/contributions/ui/VersionInstalledTableCellEditor.java
@@ -0,0 +1,112 @@
+/*
+ * 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.packages.contributions.ui;
+
+import java.awt.Component;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+import javax.swing.AbstractCellEditor;
+import javax.swing.JButton;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JTable;
+import javax.swing.table.TableCellEditor;
+
+@SuppressWarnings("serial")
+public class VersionInstalledTableCellEditor extends AbstractCellEditor
+ implements TableCellEditor {
+
+ private JPanel panel;
+ private JLabel versionLabel;
+ private JButton button;
+
+ private int currRow;
+ private Object currValue;
+ private Listener listener = null;
+
+ public VersionInstalledTableCellEditor() {
+ versionLabel = new JLabel("-");
+ versionLabel.setOpaque(false);
+
+ button = new JButton("Remove");
+ button.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ if (listener == null)
+ return;
+ listener.onRemoveEvent(currRow);
+ }
+ });
+
+ panel = new JPanel();
+ panel.add(versionLabel);
+ panel.add(button);
+ }
+
+ @Override
+ public Component getTableCellEditorComponent(JTable table, Object value,
+ boolean isSelected, int row,
+ int col) {
+ currRow = row;
+ currValue = value;
+
+ versionLabel.setText((String) value);
+
+ if (value.equals("-")) {
+ button.setVisible(false);
+ } else {
+ button.setVisible(true);
+ }
+
+ if (table.getRowSelectionAllowed()) {
+ panel.setBackground(table.getSelectionBackground());
+ panel.setForeground(table.getSelectionForeground());
+ } else {
+ panel.setBackground(table.getBackground());
+ panel.setForeground(table.getForeground());
+ }
+
+ return panel;
+ }
+
+ @Override
+ public Object getCellEditorValue() {
+ return currValue;
+ }
+
+ public static interface Listener {
+ public void onRemoveEvent(int row);
+ }
+
+ public void setListener(Listener newListener) {
+ listener = newListener;
+ }
+
+}
diff --git a/app/src/cc/arduino/packages/contributions/ui/VersionInstalledTableCellRenderer.java b/app/src/cc/arduino/packages/contributions/ui/VersionInstalledTableCellRenderer.java
new file mode 100644
index 000000000..5a7ba2ef0
--- /dev/null
+++ b/app/src/cc/arduino/packages/contributions/ui/VersionInstalledTableCellRenderer.java
@@ -0,0 +1,92 @@
+/*
+ * 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.packages.contributions.ui;
+
+import java.awt.Component;
+import java.awt.Dimension;
+
+import javax.swing.JButton;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JTable;
+import javax.swing.table.TableCellRenderer;
+import javax.swing.table.TableColumn;
+
+public class VersionInstalledTableCellRenderer implements TableCellRenderer {
+
+ private JPanel panel;
+ private JLabel versionLabel;
+ private JButton button;
+ private int minCellWidth;
+
+ public VersionInstalledTableCellRenderer() {
+ versionLabel = new JLabel("-");
+ versionLabel.setOpaque(false);
+
+ button = new JButton("Remove");
+ minCellWidth = button.getPreferredSize().width;
+
+ panel = new JPanel();
+ panel.add(versionLabel);
+ panel.add(button);
+ }
+
+ public Component getTableCellRendererComponent(JTable table, Object value,
+ boolean isSelected,
+ boolean hasFocus, int row,
+ int col) {
+ versionLabel.setText((String) value);
+
+ Dimension labelSize = versionLabel.getPreferredSize();
+ if (minCellWidth < labelSize.width)
+ minCellWidth = labelSize.width;
+ TableColumn column = table.getColumnModel().getColumn(col);
+ if (column.getMinWidth() < minCellWidth + 20) {
+ column.setMinWidth(minCellWidth + 20);
+ column.setMaxWidth(minCellWidth + 20);
+ }
+
+ if (value.equals("-")) {
+ button.setVisible(false);
+ } else {
+ button.setVisible(true);
+ }
+
+ if (isSelected) {
+ panel.setBackground(table.getSelectionBackground());
+ panel.setForeground(table.getSelectionForeground());
+ } else {
+ panel.setBackground(table.getBackground());
+ panel.setForeground(table.getForeground());
+ }
+
+ return panel;
+ }
+
+}
diff --git a/app/src/cc/arduino/packages/contributions/ui/VersionSelectorTableCellEditor.java b/app/src/cc/arduino/packages/contributions/ui/VersionSelectorTableCellEditor.java
new file mode 100644
index 000000000..f4820c667
--- /dev/null
+++ b/app/src/cc/arduino/packages/contributions/ui/VersionSelectorTableCellEditor.java
@@ -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.packages.contributions.ui;
+
+import java.awt.Component;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
+import java.util.List;
+
+import javax.swing.AbstractCellEditor;
+import javax.swing.ComboBoxModel;
+import javax.swing.DefaultComboBoxModel;
+import javax.swing.JButton;
+import javax.swing.JComboBox;
+import javax.swing.JPanel;
+import javax.swing.JTable;
+import javax.swing.table.TableCellEditor;
+
+import cc.arduino.packages.contributions.ui.ContributionIndexTableModel.ContributedPlatformReleases;
+
+@SuppressWarnings("serial")
+public class VersionSelectorTableCellEditor extends AbstractCellEditor
+ implements TableCellEditor {
+
+ private JPanel panel;
+ private JComboBox combo;
+ private JButton button;
+
+ private int currRow, currCol;
+ private ContributionIndexTableModel currModel = null;
+
+ private ItemListener itemListener = new ItemListener() {
+ @Override
+ public void itemStateChanged(ItemEvent e) {
+ if (currModel != null) {
+ currModel.setValueAt(getCellEditorValue(), currRow, currCol);
+ updateButtons();
+ }
+ }
+ };
+
+ private Listener listener = null;
+
+ public VersionSelectorTableCellEditor() {
+ panel = new JPanel();
+
+ combo = new JComboBox();
+ combo.setOpaque(false);
+ combo.addItemListener(itemListener);
+ panel.add(combo);
+
+ button = new JButton("Install");
+ button.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ if (listener == null)
+ return;
+ listener.onInstallEvent(currRow);
+ }
+ });
+ panel.add(button);
+ }
+
+ @Override
+ public Component getTableCellEditorComponent(JTable table, Object value,
+ boolean isSelected, int row,
+ int col) {
+ currCol = col;
+ currRow = row;
+ currModel = (ContributionIndexTableModel) table.getModel();
+ List values = currModel.getReleasesVersions(row);
+
+ ComboBoxModel model = new DefaultComboBoxModel(values.toArray());
+ model.setSelectedItem(currModel.getSelectedRelease(row).getVersion());
+ combo.setModel(model);
+
+ updateButtons();
+ if (table.getRowSelectionAllowed()) {
+ panel.setBackground(table.getSelectionBackground());
+ panel.setForeground(table.getSelectionForeground());
+ } else {
+ panel.setBackground(table.getBackground());
+ panel.setForeground(table.getForeground());
+ }
+ return panel;
+ }
+
+ private void updateButtons() {
+ ContributedPlatformReleases releases = currModel.getReleases(currRow);
+ boolean installed = releases.getInstalled() != null;
+ if (installed) {
+ if (releases.getInstalled() != releases.getSelected()) {
+ button.setText("Upgrade");
+ button.setVisible(true);
+ } else {
+ button.setVisible(false);
+ }
+ } else {
+ button.setText("Install");
+ button.setVisible(true);
+ }
+ }
+
+ @Override
+ public Object getCellEditorValue() {
+ return combo.getSelectedItem();
+ }
+
+ public static interface Listener {
+ public void onInstallEvent(int row);
+ }
+
+ public void setListener(Listener newListener) {
+ listener = newListener;
+ }
+
+}
diff --git a/app/src/cc/arduino/packages/contributions/ui/VersionSelectorTableCellRenderer.java b/app/src/cc/arduino/packages/contributions/ui/VersionSelectorTableCellRenderer.java
new file mode 100644
index 000000000..088cb96cb
--- /dev/null
+++ b/app/src/cc/arduino/packages/contributions/ui/VersionSelectorTableCellRenderer.java
@@ -0,0 +1,120 @@
+/*
+ * 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.packages.contributions.ui;
+
+import java.awt.Component;
+
+import javax.swing.ComboBoxModel;
+import javax.swing.DefaultComboBoxModel;
+import javax.swing.JButton;
+import javax.swing.JComboBox;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JTable;
+import javax.swing.table.TableCellRenderer;
+import javax.swing.table.TableColumn;
+
+import cc.arduino.packages.contributions.ui.ContributionIndexTableModel.ContributedPlatformReleases;
+
+public class VersionSelectorTableCellRenderer implements TableCellRenderer {
+
+ private JPanel panel;
+ private JComboBox versionComboBox;
+ private JLabel versionLabel;
+ private JButton button;
+ private int minCellWidth;
+
+ public VersionSelectorTableCellRenderer() {
+ panel = new JPanel();
+
+ versionComboBox = new JComboBox();
+ versionComboBox.setOpaque(false);
+ panel.add(versionComboBox);
+ versionComboBox.setVisible(false);
+
+ versionLabel = new JLabel();
+ versionLabel.setOpaque(false);
+ panel.add(versionLabel);
+
+ button = new JButton("Install");
+ minCellWidth = button.getPreferredSize().width;
+ button.setText("Upgrade");
+ int buttonWidth = button.getPreferredSize().width;
+ if (minCellWidth < buttonWidth)
+ minCellWidth = buttonWidth;
+
+ panel.add(button);
+ }
+
+ public Component getTableCellRendererComponent(JTable table, Object value,
+ boolean isSelected,
+ boolean hasFocus, int row,
+ int col) {
+ ComboBoxModel comboModel = new DefaultComboBoxModel(
+ new String[] { (String) value });
+ versionComboBox.setModel(comboModel);
+
+ versionLabel.setText((String) value);
+
+ ContributionIndexTableModel model = (ContributionIndexTableModel) table
+ .getModel();
+ ContributedPlatformReleases releases = model.getReleases(row);
+
+ boolean installed = releases.getInstalled() != null;
+ if (installed) {
+ if (releases.getInstalled() != releases.getSelected()) {
+ button.setText("Upgrade");
+ button.setVisible(true);
+ } else {
+ button.setVisible(false);
+ }
+ } else {
+ button.setText("Install");
+ button.setVisible(true);
+ }
+
+ int labelWidth = versionComboBox.getPreferredSize().width;
+ if (minCellWidth < labelWidth)
+ minCellWidth = labelWidth;
+ TableColumn column = table.getColumnModel().getColumn(col);
+ if (column.getMinWidth() < minCellWidth + 20) {
+ column.setMinWidth(minCellWidth + 20);
+ column.setMaxWidth(minCellWidth + 20);
+ }
+
+ if (isSelected) {
+ panel.setBackground(table.getSelectionBackground());
+ panel.setForeground(table.getSelectionForeground());
+ } else {
+ panel.setBackground(table.getBackground());
+ panel.setForeground(table.getForeground());
+ }
+ return panel;
+ }
+}
diff --git a/app/src/processing/app/Base.java b/app/src/processing/app/Base.java
index b48da543c..433114f72 100644
--- a/app/src/processing/app/Base.java
+++ b/app/src/processing/app/Base.java
@@ -23,6 +23,9 @@
package processing.app;
import cc.arduino.packages.DiscoveryManager;
+import cc.arduino.packages.contributions.ContributedPlatform;
+import cc.arduino.packages.contributions.ui.JContributionManagerDialog;
+import cc.arduino.packages.contributions.ui.JContributionManagerDialogListener;
import cc.arduino.view.SplashScreenHelper;
import processing.app.debug.TargetBoard;
import processing.app.debug.TargetPackage;
@@ -1107,13 +1110,48 @@ public class Base {
editor.onBoardOrPortChange();
}
+ private void openInstallBoardDialog() {
+ JContributionManagerDialog dialog = new JContributionManagerDialog(
+ activeEditor);
+ dialog.setListener(new JContributionManagerDialogListener() {
+ @Override
+ public void onCategoryChange(String category) {
+ System.out.println("Selected " + category);
+ }
+
+ @Override
+ public void onInstall(ContributedPlatform platform) {
+ BaseNoGui.indexer.install(platform);
+ }
+
+ @Override
+ public void onRemove(ContributedPlatform platform) {
+ BaseNoGui.indexer.remove(platform);
+ }
+ });
+ dialog.setCategories(Arrays.asList("Arduino", "Arduino Certified",
+ "Arduino@Heart"));
+ dialog.addContributions(BaseNoGui.indexer.getIndex());
+ dialog.setVisible(true);
+ }
+
public void rebuildBoardsMenu(JMenu toolsMenu, Editor editor) throws Exception {
+ JMenu boardsMenu = getBoardCustomMenu();
+
+ @SuppressWarnings("serial")
+ Action runInstaller = new AbstractAction("Install boards...") {
+ public void actionPerformed(ActionEvent actionevent) {
+ openInstallBoardDialog();
+ }
+ };
+ boardsMenu.add(new JMenuItem(runInstaller));
+
// If there are no platforms installed skip menu creation
if (BaseNoGui.packages.size() == 0)
return;
- JMenu boardsMenu = getBoardCustomMenu();
-
+ boardsMenu.add(new JSeparator());
+
boolean first = true;
List menuItemsToClickAfterStartup = new LinkedList();
@@ -1308,7 +1346,7 @@ public class Base {
}
private static JMenuItem selectFirstEnabledMenuItem(JMenu menu) {
- for (int i = 0; i < menu.getItemCount(); i++) {
+ for (int i = 1; i < menu.getItemCount(); i++) {
JMenuItem item = menu.getItem(i);
if (item != null && item.isEnabled()) {
return item;
diff --git a/arduino-core/src/cc/arduino/packages/contributions/ContributionsIndexer.java b/arduino-core/src/cc/arduino/packages/contributions/ContributionsIndexer.java
index 1b60f3aea..edcfbc9a9 100644
--- a/arduino-core/src/cc/arduino/packages/contributions/ContributionsIndexer.java
+++ b/arduino-core/src/cc/arduino/packages/contributions/ContributionsIndexer.java
@@ -197,4 +197,16 @@ public class ContributionsIndexer {
}
return res;
}
+
+ public ContributionsIndex getIndex() {
+ return index;
+ }
+
+ public void install(ContributedPlatform platform) {
+ // TODO Auto-generated method stub
+ }
+
+ public void remove(ContributedPlatform platform) {
+ // TODO Auto-generated method stub
+ }
}
diff --git a/arduino-core/src/processing/app/BaseNoGui.java b/arduino-core/src/processing/app/BaseNoGui.java
index 98310dd9d..7f870819d 100644
--- a/arduino-core/src/processing/app/BaseNoGui.java
+++ b/arduino-core/src/processing/app/BaseNoGui.java
@@ -69,9 +69,10 @@ public class BaseNoGui {
static Platform platform;
static File portableFolder = null;
-
static final String portableSketchbookFolder = "sketchbook";
+ static ContributionsIndexer indexer;
+
// Returns a File object for the given pathname. If the pathname
// is not absolute, it is interpreted relative to the current
// directory when starting the IDE (which is not the same as the
@@ -584,7 +585,7 @@ public class BaseNoGui {
}
static public void initPackages() throws Exception {
- ContributionsIndexer indexer = new ContributionsIndexer(BaseNoGui.getSettingsFolder());
+ indexer = new ContributionsIndexer(BaseNoGui.getSettingsFolder());
indexer.parseIndex();
indexer.syncWithFilesystem();
System.out.println(indexer);