From b7152b27e174cc338aedb9d38295bf489b1f9db2 Mon Sep 17 00:00:00 2001
From: Nick Poole <Nick.Poole@SparkFun.com>
Date: Tue, 29 Sep 2020 14:48:02 -0400
Subject: [PATCH 1/2] Implement "Recently Used Boards" Menu

---
 app/src/processing/app/Base.java | 72 +++++++++++++++++++++++++++++++-
 1 file changed, 71 insertions(+), 1 deletion(-)

diff --git a/app/src/processing/app/Base.java b/app/src/processing/app/Base.java
index 1e4819bba34..eaec369d650 100644
--- a/app/src/processing/app/Base.java
+++ b/app/src/processing/app/Base.java
@@ -122,6 +122,13 @@ public class Base {
   private List<JMenu> boardsCustomMenus;
   private List<JMenuItem> programmerMenus;
 
+  // these variables help rebuild the "recently used boards"
+  // menu on board selection
+  private HashMap<String, JMenuItem> recentBoardItems;
+  private List<JMenuItem> recentBoardsToClear = new LinkedList<>();;
+  private JMenu boardMenu;
+  private int recentBoardsJMenuIndex;
+
   private PdeKeywords pdeKeywords;
   private final List<JMenuItem> recentSketchesMenuItems = new LinkedList<>();
 
@@ -1360,6 +1367,26 @@ public void onBoardOrPortChange() {
       }
     }
 
+    // Update recent boards list in preferences
+    List<String> newRecentBoardIds = new ArrayList<String>();
+    String currentBoard = PreferencesData.get("board");  
+    for (String recentBoard : PreferencesData.getCollection("recent.boards")){
+      if (!recentBoard.equals(currentBoard)) {
+        newRecentBoardIds.add(recentBoard);
+      }
+    }
+    newRecentBoardIds.add(0, currentBoard);
+    if (newRecentBoardIds.size() == 6) {
+      newRecentBoardIds.remove(5);
+    }
+    PreferencesData.setCollection("recent.boards", newRecentBoardIds);
+    try {
+      rebuildRecentBoardsList();
+    } catch (Exception e) {
+      //TODO show error
+      e.printStackTrace();
+    }    
+
     // Update editors status bar
     for (Editor editor : editors) {
       editor.onBoardOrPortChange();
@@ -1433,9 +1460,10 @@ protected void onIndexesUpdated() throws Exception {
 
   public void rebuildBoardsMenu() throws Exception {
     boardsCustomMenus = new LinkedList<>();
+    recentBoardItems = new HashMap<String, JMenuItem>();
 
     // The first custom menu is the "Board" selection submenu
-    JMenu boardMenu = new JMenu(tr("Board"));
+    boardMenu = new JMenu(tr("Board"));
     boardMenu.putClientProperty("removeOnWindowDeactivation", true);
     MenuScroller.setScrollerFor(boardMenu).setTopFixedCount(1);
 
@@ -1458,6 +1486,16 @@ public void actionPerformed(ActionEvent actionevent) {
     }));
     boardsCustomMenus.add(boardMenu);
 
+    // Insert recently used boards menu and remember index for insertion later
+    if (PreferencesData.has("recent.boards")) {
+      // Insert menu label
+      boardMenu.add(new JSeparator());
+      JMenuItem label = new JMenuItem(tr("Recently Used Boards"));
+      label.setEnabled(false);
+      boardMenu.add(label);
+      recentBoardsJMenuIndex = boardMenu.getItemCount();
+    }
+
     // If there are no platforms installed we are done
     if (BaseNoGui.packages.size() == 0)
       return;
@@ -1555,6 +1593,23 @@ private String getPlatformUniqueId(TargetPlatform platform) {
     return platform.getId() + "_" + platform.getFolder();
   }
 
+  // clear the previous menu items from the "recently used boards"
+  // menu and repopulate with updated items
+  private void rebuildRecentBoardsList() throws Exception {
+    Collection<String> recentBoardIds = new LinkedList<>();
+    recentBoardIds = PreferencesData.getCollection("recent.boards");
+    int idxAdv = 0;
+    for (JMenuItem itemToClear : recentBoardsToClear) {
+      boardMenu.remove(itemToClear);
+    }
+    recentBoardsToClear.clear();
+    for (String boardId : recentBoardIds) {
+      boardMenu.add(recentBoardItems.get(boardId), recentBoardsJMenuIndex+idxAdv);
+      recentBoardsToClear.add(recentBoardItems.get(boardId));
+      idxAdv++;
+    }
+  }
+
   private JRadioButtonMenuItem createBoardMenusAndCustomMenus(
           final List<JMenu> boardsCustomMenus, List<JMenuItem> menuItemsToClickAfterStartup,
           Map<String, ButtonGroup> buttonGroupsMap,
@@ -1585,6 +1640,21 @@ public void actionPerformed(ActionEvent actionevent) {
 
     JRadioButtonMenuItem item = new JRadioButtonMenuItem(action);
 
+    // create an action for the "recent boards" copy of this menu item
+    // which clicks the original menu item
+    Action actionClone = new AbstractAction(board.getName()) {
+      public void actionPerformed(ActionEvent actionevent) {
+        item.setSelected(true);
+        item.getAction().actionPerformed(new ActionEvent(this, -1, ""));
+      }
+    }; 
+
+    // create a menu item for the "recent boards" menu
+    JMenuItem itemClone = new JMenuItem(actionClone);
+
+    // populate list of menuitem copies
+    recentBoardItems.put(boardId, itemClone);
+
     if (selBoard.equals(boardId) && selPackage.equals(packageName)
             && selPlatform.equals(platformName)) {
       menuItemsToClickAfterStartup.add(item);

From 2acc1f790c2b7577d657043f81faccd6301018d1 Mon Sep 17 00:00:00 2001
From: Nick Poole <Nick.Poole@SparkFun.com>
Date: Fri, 23 Oct 2020 04:22:23 -0400
Subject: [PATCH 2/2] Suggested Changes from PR 10816

- Make number of recent boards adjustable in the preferences.txt file at "recent.num_boards"
- Disable feature by entering value '0' for num_boards
- Change Recent board menu items to radio buttons
- Add "All Installed Boards" header to Boards menu
-Remove unnecessary LinkedList creation at line 1599
- Broaden checks to anticipate alterations to preferences
---
 app/src/processing/app/Base.java | 69 ++++++++++++++++++++++----------
 1 file changed, 47 insertions(+), 22 deletions(-)

diff --git a/app/src/processing/app/Base.java b/app/src/processing/app/Base.java
index eaec369d650..cbd504f387c 100644
--- a/app/src/processing/app/Base.java
+++ b/app/src/processing/app/Base.java
@@ -124,8 +124,8 @@ public class Base {
 
   // these variables help rebuild the "recently used boards"
   // menu on board selection
-  private HashMap<String, JMenuItem> recentBoardItems;
-  private List<JMenuItem> recentBoardsToClear = new LinkedList<>();;
+  private HashMap<String, JRadioButtonMenuItem> recentBoardItems;
+  private List<JRadioButtonMenuItem> recentBoardsToClear = new LinkedList<>();;
   private JMenu boardMenu;
   private int recentBoardsJMenuIndex;
 
@@ -1376,16 +1376,29 @@ public void onBoardOrPortChange() {
       }
     }
     newRecentBoardIds.add(0, currentBoard);
-    if (newRecentBoardIds.size() == 6) {
-      newRecentBoardIds.remove(5);
+
+    int numBoards = 0;
+
+    if (PreferencesData.has("recent.num_boards")) {
+      numBoards = PreferencesData.getInteger("recent.num_boards");
+    }
+
+    while (newRecentBoardIds.size() > numBoards) {
+      newRecentBoardIds.remove(newRecentBoardIds.size() - 1);
     }
     PreferencesData.setCollection("recent.boards", newRecentBoardIds);
-    try {
-      rebuildRecentBoardsList();
-    } catch (Exception e) {
-      //TODO show error
-      e.printStackTrace();
-    }    
+
+    // If recent.num_boards is 0, interpret this as the feature
+    // being turned off. There's no need to rebuild the menu
+    // because it will be hidden
+    if (numBoards > 0) {
+      try {
+        rebuildRecentBoardsList();
+      } catch (Exception e) {
+        //TODO show error
+        e.printStackTrace();
+      }    
+    }
 
     // Update editors status bar
     for (Editor editor : editors) {
@@ -1460,7 +1473,7 @@ protected void onIndexesUpdated() throws Exception {
 
   public void rebuildBoardsMenu() throws Exception {
     boardsCustomMenus = new LinkedList<>();
-    recentBoardItems = new HashMap<String, JMenuItem>();
+    recentBoardItems = new HashMap<String, JRadioButtonMenuItem>();
 
     // The first custom menu is the "Board" selection submenu
     boardMenu = new JMenu(tr("Board"));
@@ -1487,7 +1500,12 @@ public void actionPerformed(ActionEvent actionevent) {
     boardsCustomMenus.add(boardMenu);
 
     // Insert recently used boards menu and remember index for insertion later
-    if (PreferencesData.has("recent.boards")) {
+    // Check if the field exists, in case preferences got lost
+    if (!PreferencesData.has("recent.num_boards")){
+        // (default to 5)
+          PreferencesData.setInteger("recent.num_boards", 5);
+    }
+    if (PreferencesData.getInteger("recent.num_boards") > 0) {
       // Insert menu label
       boardMenu.add(new JSeparator());
       JMenuItem label = new JMenuItem(tr("Recently Used Boards"));
@@ -1502,6 +1520,9 @@ public void actionPerformed(ActionEvent actionevent) {
 
     // Separate "Install boards..." command from installed boards
     boardMenu.add(new JSeparator());
+    JMenuItem label = new JMenuItem(tr("All Installed Boards"));
+    label.setEnabled(false);
+    boardMenu.add(label);    
 
     // Generate custom menus for all platforms
     for (TargetPackage targetPackage : BaseNoGui.packages.values()) {
@@ -1596,16 +1617,18 @@ private String getPlatformUniqueId(TargetPlatform platform) {
   // clear the previous menu items from the "recently used boards"
   // menu and repopulate with updated items
   private void rebuildRecentBoardsList() throws Exception {
-    Collection<String> recentBoardIds = new LinkedList<>();
-    recentBoardIds = PreferencesData.getCollection("recent.boards");
+    Collection<String> recentBoardIds = PreferencesData.getCollection("recent.boards");
+    String currentBoard = PreferencesData.get("board");
     int idxAdv = 0;
-    for (JMenuItem itemToClear : recentBoardsToClear) {
+    for (JRadioButtonMenuItem itemToClear : recentBoardsToClear) {
       boardMenu.remove(itemToClear);
     }
     recentBoardsToClear.clear();
     for (String boardId : recentBoardIds) {
-      boardMenu.add(recentBoardItems.get(boardId), recentBoardsJMenuIndex+idxAdv);
-      recentBoardsToClear.add(recentBoardItems.get(boardId));
+      JRadioButtonMenuItem addItem = recentBoardItems.get(boardId);
+      boardMenu.add(addItem, recentBoardsJMenuIndex+idxAdv);
+      recentBoardsToClear.add(addItem);
+      addItem.setSelected(boardId.equals(currentBoard));
       idxAdv++;
     }
   }
@@ -1649,11 +1672,13 @@ public void actionPerformed(ActionEvent actionevent) {
       }
     }; 
 
-    // create a menu item for the "recent boards" menu
-    JMenuItem itemClone = new JMenuItem(actionClone);
-
-    // populate list of menuitem copies
-    recentBoardItems.put(boardId, itemClone);
+    // No need to hog memory if recent boards feature is turned off
+    if (PreferencesData.getInteger("recent.num_boards") > 0) {
+      // create a menu item for the "recent boards" menu
+      JRadioButtonMenuItem itemClone = new JRadioButtonMenuItem(actionClone);
+      // populate list of menuitem copies
+      recentBoardItems.put(boardId, itemClone);
+    }
 
     if (selBoard.equals(boardId) && selPackage.equals(packageName)
             && selPlatform.equals(platformName)) {