diff --git a/app/src/cc/arduino/contributions/ContributionsSelfCheck.java b/app/src/cc/arduino/contributions/ContributionsSelfCheck.java
index 7812f62ad7f..687323d1817 100644
--- a/app/src/cc/arduino/contributions/ContributionsSelfCheck.java
+++ b/app/src/cc/arduino/contributions/ContributionsSelfCheck.java
@@ -29,32 +29,34 @@
package cc.arduino.contributions;
+import cc.arduino.UpdatableBoardsLibsFakeURLsHandler;
import cc.arduino.contributions.libraries.LibraryInstaller;
import cc.arduino.contributions.libraries.filters.UpdatableLibraryPredicate;
import cc.arduino.contributions.packages.ContributionInstaller;
import cc.arduino.contributions.packages.filters.UpdatablePlatformPredicate;
import cc.arduino.view.NotificationPopup;
-import processing.app.Base;
-import processing.app.BaseNoGui;
-import processing.app.Editor;
-import processing.app.I18n;
+import org.apache.logging.log4j.LogManager;
+import processing.app.*;
import javax.swing.*;
import javax.swing.event.HyperlinkListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowFocusListener;
+import java.net.URL;
import java.util.TimerTask;
import static processing.app.I18n.tr;
-public class ContributionsSelfCheck extends TimerTask {
+public class ContributionsSelfCheck extends TimerTask implements NotificationPopup.OptionalButtonCallbacks {
private final Base base;
private final HyperlinkListener hyperlinkListener;
private final ContributionInstaller contributionInstaller;
private final LibraryInstaller libraryInstaller;
private final ProgressListener progressListener;
+ private final String boardsManagerURL = "http://boardsmanager/DropdownUpdatableCoresItem";
+ private final String libraryManagerURL = "http://librarymanager/DropdownUpdatableLibrariesItem";
private volatile boolean cancelled;
private volatile NotificationPopup notificationPopup;
@@ -81,13 +83,41 @@ public void run() {
return;
}
- String text;
+ boolean setAccessible = PreferencesData.getBoolean("ide.accessible");
+ final String text;
+ final String button1Name;
+ final String button2Name;
+ String openAnchorBoards = "";
+ String closeAnchorBoards = "";
+ String openAnchorLibraries = "";
+ String closeAnchorLibraries = "";
+
+ // if accessibility mode and board updates are available set the button name and clear the anchors
+ if(setAccessible && updatablePlatforms) {
+ button1Name = tr("Boards");
+ openAnchorBoards = "";
+ closeAnchorBoards = "";
+ }
+ else { // when not accessibility mode or no boards to update no button is needed
+ button1Name = null;
+ }
+
+ // if accessibility mode and libraries updates are available set the button name and clear the anchors
+ if (setAccessible && updatableLibraries) {
+ button2Name = tr("Libraries");
+ openAnchorLibraries = "";
+ closeAnchorLibraries = "";
+ }
+ else { // when not accessibility mode or no libraries to update no button is needed
+ button2Name = null;
+ }
+
if (updatableLibraries && !updatablePlatforms) {
- text = I18n.format(tr("Updates available for some of your {0}libraries{1}"), "", "");
+ text = I18n.format(tr("Updates available for some of your {0}libraries{1}"), openAnchorLibraries, closeAnchorLibraries);
} else if (!updatableLibraries && updatablePlatforms) {
- text = I18n.format(tr("Updates available for some of your {0}boards{1}"), "", "");
+ text = I18n.format(tr("Updates available for some of your {0}boards{1}"), openAnchorBoards, closeAnchorBoards);
} else {
- text = I18n.format(tr("Updates available for some of your {0}boards{1} and {2}libraries{3}"), "", "", "", "");
+ text = I18n.format(tr("Updates available for some of your {0}libraries{1} and {2}libraries{3}"), openAnchorBoards, closeAnchorBoards, openAnchorLibraries, closeAnchorLibraries);
}
if (cancelled) {
@@ -96,7 +126,13 @@ public void run() {
SwingUtilities.invokeLater(() -> {
Editor ed = base.getActiveEditor();
- notificationPopup = new NotificationPopup(ed, hyperlinkListener, text);
+ boolean accessibleIde = PreferencesData.getBoolean("ide.accessible");
+ if (accessibleIde) {
+ notificationPopup = new NotificationPopup(ed, hyperlinkListener, text, false, this, button1Name, button2Name);
+ }
+ else { // if not accessible view leave it the same
+ notificationPopup = new NotificationPopup(ed, hyperlinkListener, text);
+ }
if (ed.isFocused()) {
notificationPopup.begin();
return;
@@ -122,6 +158,24 @@ public void windowGainedFocus(WindowEvent evt) {
});
}
+ private void goToManager(String link) {
+ try {
+ ((UpdatableBoardsLibsFakeURLsHandler) hyperlinkListener).openBoardLibManager(new URL(link));
+ }
+ catch (Exception e){
+ LogManager.getLogger(ContributionsSelfCheck.class).warn("Exception while attempting to go to board manager", e);
+ }
+ }
+ // callback for boards button
+ public void onOptionalButton1Callback() {
+ goToManager(boardsManagerURL);
+ }
+
+ // callback for libraries button
+ public void onOptionalButton2Callback() {
+ goToManager(libraryManagerURL);
+ }
+
static boolean checkForUpdatablePlatforms() {
return BaseNoGui.indexer.getPackages().stream()
.flatMap(pack -> pack.getPlatforms().stream())
diff --git a/app/src/cc/arduino/view/NotificationPopup.java b/app/src/cc/arduino/view/NotificationPopup.java
index 2334d6e14bd..69fb3ff9ec0 100644
--- a/app/src/cc/arduino/view/NotificationPopup.java
+++ b/app/src/cc/arduino/view/NotificationPopup.java
@@ -36,12 +36,7 @@
import java.awt.Frame;
import java.awt.Image;
import java.awt.Point;
-import java.awt.event.ComponentAdapter;
-import java.awt.event.ComponentEvent;
-import java.awt.event.MouseAdapter;
-import java.awt.event.MouseEvent;
-import java.awt.event.WindowAdapter;
-import java.awt.event.WindowEvent;
+import java.awt.event.*;
import java.util.Timer;
import java.util.TimerTask;
@@ -55,22 +50,46 @@
import javax.swing.event.HyperlinkListener;
import cc.arduino.Constants;
+import processing.app.PreferencesData;
import processing.app.Theme;
-public class NotificationPopup extends JDialog {
+import java.awt.event.KeyEvent;
+
+import static processing.app.I18n.tr;
+public class NotificationPopup extends JDialog {
private Timer autoCloseTimer = new Timer(false);
private boolean autoClose = true;
+ private OptionalButtonCallbacks optionalButtonCallbacks;
+
+ public interface OptionalButtonCallbacks {
+ void onOptionalButton1Callback();
+ void onOptionalButton2Callback();
+ }
public NotificationPopup(Frame parent, HyperlinkListener hyperlinkListener,
String message) {
- this(parent, hyperlinkListener, message, true);
+ this(parent, hyperlinkListener, message, true, null, null, null);
}
public NotificationPopup(Frame parent, HyperlinkListener hyperlinkListener,
String message, boolean _autoClose) {
+ this(parent, hyperlinkListener, message, _autoClose, null, null, null);
+ }
+
+ public NotificationPopup(Frame parent, HyperlinkListener hyperlinkListener,
+ String message, boolean _autoClose, OptionalButtonCallbacks listener, String button1Name, String button2Name) {
super(parent, false);
- autoClose = _autoClose;
+
+ if (!PreferencesData.getBoolean("ide.accessible")) {
+ // often auto-close is too fast for users of screen readers, so don't allow it.
+ autoClose = _autoClose;
+ }
+ else {
+ autoClose = false;
+ }
+ optionalButtonCallbacks = listener;
+
setLayout(new FlowLayout());
setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
setUndecorated(true);
@@ -90,6 +109,74 @@ public NotificationPopup(Frame parent, HyperlinkListener hyperlinkListener,
text.addHyperlinkListener(hyperlinkListener);
add(text);
+ if (button1Name != null) {
+ JButton optionalButton1 = new JButton(tr(button1Name));
+ MouseAdapter button1Action = new MouseAdapter() {
+ @Override
+ public void mouseClicked(MouseEvent e) {
+ if (optionalButtonCallbacks != null) {
+ optionalButtonCallbacks.onOptionalButton1Callback();
+ }
+ }
+ };
+ optionalButton1.addMouseListener(button1Action);
+
+ KeyListener button1Key = new KeyListener() {
+ // Ignore when the key is typed - only act once the key is released
+ public void keyTyped(KeyEvent e) {
+ // do nothing here, wait until the key is released
+ }
+
+ // Ignore when the key is pressed - only act once the key is released
+ public void keyPressed(KeyEvent e) {
+ // do nothing here, wait until the key is released
+ }
+
+ public void keyReleased(KeyEvent e) {
+ int key = e.getKeyCode();
+ if ((key == KeyEvent.VK_ENTER) || (key == KeyEvent.VK_SPACE)) {
+ optionalButtonCallbacks.onOptionalButton1Callback();
+ }
+ }
+ };
+ optionalButton1.addKeyListener(button1Key);
+ add(optionalButton1);
+ }
+
+ if (button2Name != null) {
+ JButton optionalButton2 = new JButton(tr(button2Name));
+ MouseAdapter button2Action = new MouseAdapter() {
+ @Override
+ public void mouseClicked(MouseEvent e) {
+ if (optionalButtonCallbacks != null) {
+ optionalButtonCallbacks.onOptionalButton2Callback();
+ }
+ }
+ };
+ optionalButton2.addMouseListener(button2Action);
+
+ KeyListener button2Key = new KeyListener() {
+ // Ignore when the key is typed - only act once the key is released
+ public void keyTyped(KeyEvent e) {
+ // do nothing here, wait until the key is released
+ }
+
+ // Ignore when the key is pressed - only act once the key is released
+ public void keyPressed(KeyEvent e) {
+ // do nothing here, wait until the key is released
+ }
+
+ public void keyReleased(KeyEvent e) {
+ int key = e.getKeyCode();
+ if ((key == KeyEvent.VK_ENTER) || (key == KeyEvent.VK_SPACE)) {
+ optionalButtonCallbacks.onOptionalButton2Callback();
+ }
+ }
+ };
+ optionalButton2.addKeyListener(button2Key);
+ add(optionalButton2);
+ }
+
Image close = Theme.getThemeImage("close", this, scale(22), scale(22));
JButton closeButton = new JButton(new ImageIcon(close));
closeButton.setBorder(null);
@@ -97,6 +184,26 @@ public NotificationPopup(Frame parent, HyperlinkListener hyperlinkListener,
closeButton.setHideActionText(true);
closeButton.setOpaque(false);
closeButton.setBackground(new Color(0, 0, 0, 0));
+ closeButton.getAccessibleContext().setAccessibleDescription(tr("Close"));
+ KeyListener closeKey = new KeyListener() {
+ // Ignore when the key is typed - only act once the key is released
+ public void keyTyped(KeyEvent e) {
+ // do nothing here, wait until the key is released
+ }
+
+ // Ignore when the key is pressed - only act once the key is released
+ public void keyPressed(KeyEvent e) {
+ // do nothing here, wait until the key is released
+ }
+
+ public void keyReleased(KeyEvent e) {
+ int key = e.getKeyCode();
+ if ((key == KeyEvent.VK_ENTER) || (key == KeyEvent.VK_SPACE)) {
+ close();
+ }
+ }
+ };
+ closeButton.addKeyListener(closeKey);
add(closeButton);
MouseAdapter closeOnClick = new MouseAdapter() {
@@ -158,5 +265,9 @@ public void run() {
}, Constants.NOTIFICATION_POPUP_AUTOCLOSE_DELAY);
}
setVisible(true);
+ if (PreferencesData.getBoolean("ide.accessible")) {
+ requestFocus();
+ setModal(true);
+ }
}
}
diff --git a/app/src/cc/arduino/view/preferences/Preferences.java b/app/src/cc/arduino/view/preferences/Preferences.java
index 736ae826cfe..8711ac8b577 100644
--- a/app/src/cc/arduino/view/preferences/Preferences.java
+++ b/app/src/cc/arduino/view/preferences/Preferences.java
@@ -134,6 +134,7 @@ private void initComponents() {
externalEditorBox = new javax.swing.JCheckBox();
checkUpdatesBox = new javax.swing.JCheckBox();
saveVerifyUploadBox = new javax.swing.JCheckBox();
+ accessibleIDEBox = new javax.swing.JCheckBox();
jLabel1 = new javax.swing.JLabel();
jLabel2 = new javax.swing.JLabel();
scaleSpinner = new javax.swing.JSpinner();
@@ -281,6 +282,9 @@ public void mouseEntered(java.awt.event.MouseEvent evt) {
saveVerifyUploadBox.setText(tr("Save when verifying or uploading"));
checkboxesContainer.add(saveVerifyUploadBox);
+ accessibleIDEBox.setText(tr("Use accessibility features"));
+ checkboxesContainer.add(accessibleIDEBox);
+
jLabel1.setText(tr("Interface scale:"));
jLabel2.setText(tr(" (requires restart of Arduino)"));
@@ -713,6 +717,7 @@ private void autoScaleCheckBoxItemStateChanged(java.awt.event.ItemEvent evt) {//
private javax.swing.JCheckBox autoScaleCheckBox;
private javax.swing.JButton browseButton;
private javax.swing.JCheckBox checkUpdatesBox;
+ private javax.swing.JCheckBox accessibleIDEBox;
private javax.swing.JPanel checkboxesContainer;
private javax.swing.JComboBox comboLanguage;
private javax.swing.JLabel comboLanguageLabel;
@@ -826,7 +831,7 @@ private void savePreferencesData() {
PreferencesData.setBoolean("update.check", checkUpdatesBox.isSelected());
- PreferencesData.setBoolean("editor.save_on_verify", saveVerifyUploadBox.isSelected());
+ PreferencesData.setBoolean("ide.accessible", accessibleIDEBox.isSelected());
PreferencesData.set("boardsmanager.additional.urls", additionalBoardsManagerField.getText().replace("\r\n", "\n").replace("\r", "\n").replace("\n", ","));
@@ -902,6 +907,8 @@ private void showPreferencesData() {
PreferencesData.setBoolean("editor.update_extension", true);
}
+ accessibleIDEBox.setSelected(PreferencesData.getBoolean("ide.accessible"));
+
saveVerifyUploadBox.setSelected(PreferencesData.getBoolean("editor.save_on_verify"));
additionalBoardsManagerField.setText(PreferencesData.get("boardsmanager.additional.urls"));