diff --git a/.gitignore b/.gitignore
index a132b332d..4a1b6b4c7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -53,3 +53,11 @@ test-bin
.directory
hardware/arduino/avr/libraries/Bridge/examples/XivelyClient/passwords.h
avr-toolchain-*.zip
+/app/nbproject/private/
+/arduino-core/nbproject/private/
+/app/build/
+/arduino-core/build/
+
+manifest.mf
+nbbuild.xml
+nbproject
diff --git a/app/src/cc/arduino/view/preferences/AdditionalBoardsManagerURLTextArea.form b/app/src/cc/arduino/view/preferences/AdditionalBoardsManagerURLTextArea.form
new file mode 100644
index 000000000..0d47750a3
--- /dev/null
+++ b/app/src/cc/arduino/view/preferences/AdditionalBoardsManagerURLTextArea.form
@@ -0,0 +1,108 @@
+
+
+
diff --git a/app/src/cc/arduino/view/preferences/AdditionalBoardsManagerURLTextArea.java b/app/src/cc/arduino/view/preferences/AdditionalBoardsManagerURLTextArea.java
new file mode 100644
index 000000000..e09574e9b
--- /dev/null
+++ b/app/src/cc/arduino/view/preferences/AdditionalBoardsManagerURLTextArea.java
@@ -0,0 +1,215 @@
+/*
+ * This file is part of Arduino.
+ *
+ * Copyright 2015 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.view.preferences;
+
+import com.google.common.base.Function;
+import com.google.common.base.Joiner;
+import com.google.common.base.Predicate;
+import com.google.common.collect.FluentIterable;
+import processing.app.Base;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.WindowEvent;
+import java.util.Arrays;
+import java.util.Collection;
+
+import static processing.app.I18n._;
+
+public class AdditionalBoardsManagerURLTextArea extends javax.swing.JDialog {
+
+ private ActionListener onOkListener;
+
+ public AdditionalBoardsManagerURLTextArea(java.awt.Frame parent) {
+ super(parent);
+ initComponents();
+ setLocationRelativeTo(parent);
+
+ Base.registerWindowCloseKeys(getRootPane(), new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ cancelActionPerformed(e);
+ }
+ });
+
+ }
+
+ /**
+ * This method is called from within the constructor to initialize the form.
+ * WARNING: Do NOT modify this code. The content of this method is always
+ * regenerated by the Form Editor.
+ */
+ @SuppressWarnings("unchecked")
+ // //GEN-BEGIN:initComponents
+ private void initComponents() {
+
+ javax.swing.JScrollPane jScrollPane1 = new javax.swing.JScrollPane();
+ javax.swing.JButton cancel = new javax.swing.JButton();
+ javax.swing.JButton ok = new javax.swing.JButton();
+
+ setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
+ setTitle(_("Additional Boards Manager URLs"));
+ setModal(true);
+ setModalExclusionType(java.awt.Dialog.ModalExclusionType.APPLICATION_EXCLUDE);
+
+ additionalBoardsManagerURLs.setColumns(20);
+ additionalBoardsManagerURLs.setRows(5);
+ additionalBoardsManagerURLs.setName(""); // NOI18N
+ jScrollPane1.setViewportView(additionalBoardsManagerURLs);
+
+ cancel.setText(_("Cancel"));
+ cancel.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ cancelActionPerformed(evt);
+ }
+ });
+
+ ok.setText(_("OK"));
+ ok.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ okActionPerformed(evt);
+ }
+ });
+
+ javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
+ getContentPane().setLayout(layout);
+ layout.setHorizontalGroup(
+ layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(layout.createSequentialGroup()
+ .addContainerGap()
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(layout.createSequentialGroup()
+ .addGap(0, 439, Short.MAX_VALUE)
+ .addComponent(ok)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(cancel))
+ .addComponent(jScrollPane1))
+ .addContainerGap())
+ );
+ layout.setVerticalGroup(
+ layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(layout.createSequentialGroup()
+ .addContainerGap()
+ .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 141, Short.MAX_VALUE)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(cancel)
+ .addComponent(ok))
+ .addContainerGap())
+ );
+
+ pack();
+ }// //GEN-END:initComponents
+
+ private void cancelActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cancelActionPerformed
+ dispatchEvent(new WindowEvent(this, WindowEvent.WINDOW_CLOSING));
+ }//GEN-LAST:event_cancelActionPerformed
+
+ private void okActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_okActionPerformed
+ ActionEvent actionEvent = new ActionEvent(this, ActionEvent.ACTION_PERFORMED, "");
+ onOkListener.actionPerformed(actionEvent);
+ cancelActionPerformed(evt);
+ }//GEN-LAST:event_okActionPerformed
+
+ /**
+ * @param args the command line arguments
+ */
+ public static void main(String args[]) {
+ /* Set the Nimbus look and feel */
+ //
+ /* If Nimbus (introduced in Java SE 6) is not available, stay with the default look and feel.
+ * For details see http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html
+ */
+ try {
+ for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
+ if ("Nimbus".equals(info.getName())) {
+ javax.swing.UIManager.setLookAndFeel(info.getClassName());
+ break;
+ }
+ }
+ } catch (ClassNotFoundException ex) {
+ java.util.logging.Logger.getLogger(AdditionalBoardsManagerURLTextArea.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
+ } catch (InstantiationException ex) {
+ java.util.logging.Logger.getLogger(AdditionalBoardsManagerURLTextArea.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
+ } catch (IllegalAccessException ex) {
+ java.util.logging.Logger.getLogger(AdditionalBoardsManagerURLTextArea.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
+ } catch (javax.swing.UnsupportedLookAndFeelException ex) {
+ java.util.logging.Logger.getLogger(AdditionalBoardsManagerURLTextArea.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
+ }
+ //
+
+ /* Create and display the dialog */
+ java.awt.EventQueue.invokeLater(new Runnable() {
+ public void run() {
+ AdditionalBoardsManagerURLTextArea dialog = new AdditionalBoardsManagerURLTextArea(new javax.swing.JFrame());
+ dialog.addWindowListener(new java.awt.event.WindowAdapter() {
+ @Override
+ public void windowClosing(java.awt.event.WindowEvent e) {
+ System.exit(0);
+ }
+ });
+ dialog.setVisible(true);
+ }
+ });
+ }
+
+ public void setText(String text) {
+ Collection urls = splitAndTrim(text, ",");
+ additionalBoardsManagerURLs.setText(Joiner.on("\n").skipNulls().join(urls));
+ }
+
+ private Collection splitAndTrim(String text, String separator) {
+ Collection urls = Arrays.asList(text.split(separator));
+ return FluentIterable.from(urls).transform(new Function() {
+ @Override
+ public String apply(String input) {
+ return input.trim();
+ }
+ }).filter(new Predicate() {
+ @Override
+ public boolean apply(String input) {
+ return !input.isEmpty();
+ }
+ }).toList();
+ }
+
+ public String getText() {
+ Collection urls = splitAndTrim(additionalBoardsManagerURLs.getText(), "\n");
+ return Joiner.on(",").skipNulls().join(urls);
+ }
+
+ // Variables declaration - do not modify//GEN-BEGIN:variables
+ private final javax.swing.JTextArea additionalBoardsManagerURLs = new javax.swing.JTextArea();
+ // End of variables declaration//GEN-END:variables
+
+ public void onOk(ActionListener listener) {
+ this.onOkListener = listener;
+ }
+}
diff --git a/app/src/processing/app/Preferences.java b/app/src/processing/app/Preferences.java
index 7d361c41d..f55b976af 100644
--- a/app/src/processing/app/Preferences.java
+++ b/app/src/processing/app/Preferences.java
@@ -21,6 +21,11 @@
package processing.app;
+import cc.arduino.view.preferences.AdditionalBoardsManagerURLTextArea;
+import com.google.common.base.Function;
+import com.google.common.base.Functions;
+import com.google.common.base.Joiner;
+import com.google.common.collect.Collections2;
import processing.app.helpers.FileUtils;
import processing.app.helpers.OSUtils;
import processing.app.helpers.PreferencesHelper;
@@ -31,6 +36,8 @@ import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.io.File;
+import java.util.*;
+import java.util.List;
import static processing.app.I18n._;
@@ -489,6 +496,22 @@ public class Preferences {
box.add(label);
additionalBoardsManagerField = new JTextField(30);
box.add(additionalBoardsManagerField);
+ JButton extendedAdditionalUrlFieldWindow = new JButton(new ImageIcon(Base.getThemeImage("newwindow.gif", dialog)));
+ extendedAdditionalUrlFieldWindow.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ final AdditionalBoardsManagerURLTextArea additionalBoardsManagerURLTextArea = new AdditionalBoardsManagerURLTextArea(dialog);
+ additionalBoardsManagerURLTextArea.setText(additionalBoardsManagerField.getText());
+ additionalBoardsManagerURLTextArea.onOk(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ additionalBoardsManagerField.setText(additionalBoardsManagerURLTextArea.getText());
+ }
+ });
+ additionalBoardsManagerURLTextArea.setVisible(true);
+ }
+ });
+ box.add(extendedAdditionalUrlFieldWindow);
pane.add(box);
d = box.getPreferredSize();
box.setBounds(left, top, d.width, d.height);
diff --git a/build/shared/lib/theme/newwindow.gif b/build/shared/lib/theme/newwindow.gif
new file mode 100644
index 000000000..1cf77a620
Binary files /dev/null and b/build/shared/lib/theme/newwindow.gif differ