Merge pull request #2795 from joelmoniz/addExamplesManagerRebased

Added examples-package as a new contribution type.
This commit is contained in:
Ben Fry
2014-08-19 13:27:36 -04:00
6 changed files with 381 additions and 26 deletions
+40 -1
View File
@@ -98,6 +98,7 @@ public class Base {
ContributionManagerDialog libraryManagerFrame;
ContributionManagerDialog toolManagerFrame;
ContributionManagerDialog modeManagerFrame;
ContributionManagerDialog exampleManagerFrame;
ContributionManagerDialog updateManagerFrame;
// set to true after the first time the menu is built.
@@ -123,6 +124,8 @@ public class Base {
private Mode[] coreModes;
//public List<ModeContribution> contribModes;
protected ArrayList<ModeContribution> modeContribs;
protected ArrayList<ExamplesPackageContribution> exampleContribs;
private JMenu sketchbookMenu;
@@ -339,6 +342,19 @@ public class Base {
}
/**
* Instantiates and adds new contributed modes to the contribModes list.
* Checks for duplicates so the same mode isn't instantiates twice. Does not
* remove modes because modes can't be removed once they are instantiated.
*/
void rebuildContribExamples() {
if (exampleContribs == null) {
exampleContribs = new ArrayList<ExamplesPackageContribution>();
}
ExamplesPackageContribution.loadMissing(this);
}
public Base(String[] args) throws Exception {
// // Get the sketchbook path, and make sure it's set properly
// determineSketchbookFolder();
@@ -356,6 +372,8 @@ public class Base {
ContributionManager.cleanup(this);
buildCoreModes();
rebuildContribModes();
rebuildContribExamples();
// Needs to happen after the sketchbook folder has been located.
// Also relies on the modes to be loaded so it knows what can be
@@ -385,6 +403,8 @@ public class Base {
new ContributionManagerDialog(ContributionType.TOOL);
modeManagerFrame =
new ContributionManagerDialog(ContributionType.MODE);
exampleManagerFrame =
new ContributionManagerDialog(ContributionType.EXAMPLES_PACKAGE);
updateManagerFrame =
new ContributionManagerDialog(null);
@@ -658,6 +678,11 @@ public class Base {
}
public ArrayList<ExamplesPackageContribution> getExampleContribs() {
return exampleContribs;
}
// Because of variations in native windowing systems, no guarantees about
// changes to the focused and active Windows can be made. Developers must
// never assume that this Window is the focused or active Window until this
@@ -1483,7 +1508,7 @@ public class Base {
JMenu submenu = new JMenu(name);
// needs to be separate var otherwise would set ifound to false
boolean anything = addSketches(submenu, subfolder, replaceExisting);
if (anything) {
if (anything && !name.equals("old")) { //Don't add old contributions
menu.add(submenu);
found = true;
}
@@ -1651,6 +1676,14 @@ public class Base {
}
/**
* Show the examples installer window.
*/
public void handleOpenExampleManager() {
exampleManagerFrame.showFrame(activeEditor);
}
public void handleShowUpdates() {
updateManagerFrame.showFrame(activeEditor);
}
@@ -1923,6 +1956,7 @@ public class Base {
getSketchbookLibrariesFolder().mkdir();
getSketchbookToolsFolder().mkdir();
getSketchbookModesFolder().mkdir();
getSketchbookExamplesPackagesFolder().mkdir();
// System.err.println("sketchbook: " + sketchbookFolder);
}
@@ -1954,6 +1988,11 @@ public class Base {
}
static public File getSketchbookExamplesPackagesFolder() {
return new File(sketchbookFolder, "examples-packages");
}
static protected File getDefaultSketchbookFolder() {
File sketchbookFolder = null;
try {
+215 -20
View File
@@ -29,11 +29,15 @@ import java.io.*;
import java.util.*;
import javax.swing.*;
import javax.swing.border.Border;
import javax.swing.border.EmptyBorder;
import javax.swing.event.TreeExpansionEvent;
import javax.swing.event.TreeExpansionListener;
import javax.swing.plaf.basic.BasicTreeUI;
import javax.swing.tree.*;
import processing.app.contrib.ContributionType;
import processing.app.contrib.ExamplesPackageContribution;
import processing.app.syntax.*;
import processing.core.PApplet;
@@ -69,6 +73,8 @@ public abstract class Mode {
protected File examplesFolder;
protected File librariesFolder;
protected File referenceFolder;
protected File examplesContribFolder;
public ArrayList<Library> coreLibraries;
public ArrayList<Library> contribLibraries;
@@ -100,6 +106,9 @@ public abstract class Mode {
examplesFolder = new File(folder, "examples");
librariesFolder = new File(folder, "libraries");
referenceFolder = new File(folder, "reference");
// Get path to the contributed examples compatible with this mode
examplesContribFolder = Base.getSketchbookExamplesPackagesFolder();
// rebuildToolbarMenu();
rebuildLibraryList();
@@ -404,6 +413,14 @@ public abstract class Mode {
}
});
toolbarMenu.add(item);
item = new JMenuItem("Add Examples...");
item.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
base.handleOpenExampleManager();
}
});
toolbarMenu.add(item);
// Add a list of all sketches and subfolders
toolbarMenu.addSeparator();
@@ -588,10 +605,10 @@ public abstract class Mode {
}
public JTree buildExamplesTree() {
public DefaultMutableTreeNode buildExamplesTree() {
DefaultMutableTreeNode node = new DefaultMutableTreeNode("Examples");
JTree examplesTree = new JTree(node);
// JTree examplesTree = new JTree(node);
// rebuildExamplesTree(node);
// }
@@ -610,30 +627,28 @@ public abstract class Mode {
// });
File[] subfolders = getExampleCategoryFolders();
// DefaultMutableTreeNode examplesParent = new DefaultMutableTreeNode("Examples");
DefaultMutableTreeNode modeExParent = new DefaultMutableTreeNode("Mode Examples");
for (File sub : subfolders) {
DefaultMutableTreeNode subNode = new DefaultMutableTreeNode(sub.getName());
if (base.addSketches(subNode, sub)) {
// examplesParent.add(subNode);
node.add(subNode);
modeExParent.add(subNode);
}
}
// node.add(examplesParent);
// examplesTree.expandPath(new TreePath(examplesParent));
// get library examples
boolean any = false;
DefaultMutableTreeNode libParent = new DefaultMutableTreeNode("Libraries");
for (Library lib : coreLibraries) {
if (lib.hasExamples()) {
DefaultMutableTreeNode libNode = new DefaultMutableTreeNode(lib.getName());
any |= base.addSketches(libNode, lib.getExamplesFolder());
libParent.add(libNode);
if (base.addSketches(libNode, lib.getExamplesFolder()))
modeExParent.add(libNode);
}
}
if (any) {
node.add(libParent);
}
if (modeExParent.getChildCount() > 0)
node.add(modeExParent);
// get contrib library examples
any = false;
@@ -644,7 +659,7 @@ public abstract class Mode {
}
if (any) {
// menu.addSeparator();
DefaultMutableTreeNode contribParent = new DefaultMutableTreeNode("Contributed Libraries");
DefaultMutableTreeNode contribParent = new DefaultMutableTreeNode("Library Examples");
// Base.addDisabledItem(menu, "Contributed");
for (Library lib : contribLibraries) {
if (lib.hasExamples()) {
@@ -661,7 +676,46 @@ public abstract class Mode {
} catch (IOException e) {
e.printStackTrace();
}
return examplesTree;
DefaultMutableTreeNode contribExampleNode = buildContributedExamplesTrees();
if (contribExampleNode.getChildCount() > 0)
node.add(contribExampleNode);
return node;
}
public DefaultMutableTreeNode buildContributedExamplesTrees() {
DefaultMutableTreeNode node = new DefaultMutableTreeNode("Contributed Examples");
try {
File[] subfolders = ContributionType.EXAMPLES_PACKAGE.listCandidates(examplesContribFolder);
if (subfolders == null) {
subfolders = new File[0]; //empty array
}
for (File sub : subfolders) {
if (!ExamplesPackageContribution.isExamplesPackageCompatible(base, sub))
continue;
DefaultMutableTreeNode subNode = new DefaultMutableTreeNode(sub.getName());
if (base.addSketches(subNode, sub)) {
node.add(subNode);
int exampleNodeNumber = -1;
for (int y = 0; y < subNode.getChildCount(); y++)
if (subNode.getChildAt(y).toString().equals("examples-package"))
exampleNodeNumber = y;
if (exampleNodeNumber == -1)
continue;
TreeNode exampleNode = subNode.getChildAt(exampleNodeNumber);
subNode.remove(exampleNodeNumber);
int count = exampleNode.getChildCount();
for (int x = 0; x < count; x++) {
subNode.add((DefaultMutableTreeNode) exampleNode.getChildAt(0));
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
return node;//examplesTree;
}
@@ -682,6 +736,98 @@ public abstract class Mode {
}
/**
* Function to give a JTree a pretty alternating gray-white colouring for
* its rows.
*
* @param tree
*/
private void colourizeTreeRows(JTree tree) {
// Code in this function adapted from:
// http://mateuszstankiewicz.eu/?p=263
tree.setCellRenderer(new DefaultTreeCellRenderer() {
@Override
public Component getTreeCellRendererComponent(JTree tree, Object value,
boolean sel,
boolean expanded,
boolean leaf, int row,
boolean hasFocus) {
JComponent c = (JComponent) super
.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row,
hasFocus);
if (!tree.isRowSelected(row)) {
if (row % 2 == 0) {
// Need to set this, else the gray from the odd
// rows colours this gray as well.
c.setBackground(new Color(255, 255, 255));
setBackgroundSelectionColor(new Color(0, 0, 255));
setTextSelectionColor(Color.WHITE);
setBorderSelectionColor(new Color(0, 0, 255));
} else {
// Set background for entire component (including the image).
// Using transparency messes things up, probably since the
// transparent colour is not good friends with the images background colour.
c.setBackground(new Color(240, 240, 240));
// Can't use setBackgroundSelectionColor() directly, since then, the
// image's background isn't affected.
// The setUI() doesn't fix the image's background because the
// transparency likely interferes with its normal background,
// making its background lighter than the rest.
// setBackgroundNonSelectionColor(new Color(190, 190, 190));
setBackgroundSelectionColor(new Color(0, 0, 255));
setTextSelectionColor(Color.WHITE);
setBorderSelectionColor(new Color(0, 0, 255));
}
} else {// Transparent blue if selected
c.setBackground(new Color(127, 127, 255));
}
c.setOpaque(true);
return c;
}
});
tree.setUI(new BasicTreeUI() {
@Override
protected void paintRow(Graphics g, Rectangle clipBounds, Insets insets,
Rectangle bounds, TreePath path, int row,
boolean isExpanded, boolean hasBeenExpanded,
boolean isLeaf) {
Graphics g2 = g.create();
if (!tree.isRowSelected(row)) {
if (row % 2 == 0) {
// Need to set this, else the gray from the odd rows
// affects the even rows too.
g2.setColor(new Color(255, 255, 255, 128));
} else {
// Transparent light-gray
g2.setColor(new Color(226, 226, 226, 128));
}
} else
// Transparent blue if selected
g2.setColor(new Color(0, 0, 255, 128));
g2.fillRect(0, bounds.y, tree.getWidth(), bounds.height);
g2.dispose();
super.paintRow(g, clipBounds, insets, bounds, path, row, isExpanded,
hasBeenExpanded, isLeaf);
}
});
}
public void showExamplesFrame() {
if (examplesFrame == null) {
examplesFrame = new JFrame(getTitle() + " " + Language.text("examples"));
@@ -692,7 +838,49 @@ public abstract class Mode {
}
});
final JTree tree = buildExamplesTree();
JPanel examplesPanel = new JPanel();
examplesPanel.setLayout(new BorderLayout());
examplesPanel.setBackground(Color.WHITE);
final JPanel openExamplesManagerPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
JLabel openExamplesManagerLabel = new JLabel("Add Examples...");
// openExamplesManagerLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
openExamplesManagerPanel.add(openExamplesManagerLabel);
openExamplesManagerPanel.setOpaque(false);
Border lineBorder = BorderFactory.createMatteBorder(0, 0, 1, 0, Color.BLACK);
Border paddingBorder = BorderFactory.createEmptyBorder(3, 5, 1, 4);
openExamplesManagerPanel.setBorder(BorderFactory.createCompoundBorder(lineBorder, paddingBorder));
// openExamplesManagerLabel.set
openExamplesManagerPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
openExamplesManagerPanel.setCursor(new Cursor(Cursor.HAND_CURSOR));
// openExamplesManagerLabel.setForeground(new Color(0, 0, 238));
openExamplesManagerPanel.addMouseListener(new MouseListener() {
@Override
public void mouseReleased(MouseEvent e) {}
@Override
public void mousePressed(MouseEvent e) {}
@Override
public void mouseExited(MouseEvent e) {}
@Override
public void mouseEntered(MouseEvent e) {}
@Override
public void mouseClicked(MouseEvent e) {
base.handleOpenExampleManager();
// openExamplesManagerLabel.setForeground(new Color(85, 26, 139));
}
});
final JTree tree = new JTree(buildExamplesTree());
colourizeTreeRows(tree);
tree.setOpaque(true);
tree.setAlignmentX(Component.LEFT_ALIGNMENT);
tree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
tree.setShowsRootHandles(true);
@@ -754,16 +942,23 @@ public abstract class Mode {
}
});
tree.setBorder(new EmptyBorder(5, 5, 5, 5));
tree.setBorder(new EmptyBorder(0, 5, 5, 5));
if (Base.isMacOS()) {
tree.setToggleClickCount(2);
} else {
tree.setToggleClickCount(1);
}
JScrollPane treePane = new JScrollPane(tree);
treePane.setPreferredSize(new Dimension(250, 450));
treePane.setBorder(new EmptyBorder(0, 0, 0, 0));
examplesFrame.getContentPane().add(treePane);
treePane.setPreferredSize(new Dimension(250, 300));
treePane.setBorder(new EmptyBorder(2, 0, 0, 0));
treePane.setOpaque(true);
treePane.setBackground(Color.WHITE);
treePane.setAlignmentX(Component.LEFT_ALIGNMENT);
examplesPanel.add(openExamplesManagerPanel,BorderLayout.PAGE_START);
examplesPanel.add(treePane, BorderLayout.CENTER);
examplesFrame.getContentPane().add(examplesPanel);
examplesFrame.pack();
restoreExpanded(tree);
@@ -1,4 +1,4 @@
/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
Part of the Processing project - http://processing.org
@@ -268,6 +268,12 @@ class AvailableContribution extends Contribution {
String prettyVersion = properties.get("prettyVersion");
if (prettyVersion == null || prettyVersion.isEmpty())
prettyVersion = getPrettyVersion();
String compatibleContribsList = null;
if (getType() == ContributionType.EXAMPLES_PACKAGE) {
compatibleContribsList = properties.get("compatibleModesList");
}
long lastUpdated;
try {
@@ -294,6 +300,9 @@ class AvailableContribution extends Contribution {
writer.println("version=" + version);
writer.println("prettyVersion=" + prettyVersion);
writer.println("lastUpdated=" + lastUpdated);
if (getType() == ContributionType.EXAMPLES_PACKAGE) {
writer.println("compatibleModesList=" + compatibleContribsList);
}
writer.flush();
writer.close();
@@ -392,7 +392,10 @@ public class ContributionManagerDialog {
ArrayList<ModeContribution> modes = editor.getBase().getModeContribs();
contributions.addAll(modes);
ArrayList<ExamplesPackageContribution> examples = editor.getBase().getExampleContribs();
contributions.addAll(examples);
// ArrayList<LibraryCompilation> compilations = LibraryCompilation.list(libraries);
//
// // Remove libraries from the list that are part of a compilations
@@ -31,7 +31,7 @@ import processing.app.Editor;
import processing.app.Library;
public enum ContributionType {
LIBRARY, TOOL, MODE;
LIBRARY, TOOL, MODE, EXAMPLES_PACKAGE;
public String toString() {
@@ -42,6 +42,8 @@ public enum ContributionType {
return "tool";
case MODE:
return "mode";
case EXAMPLES_PACKAGE:
return "examples-package";
}
return null; // should be unreachable
};
@@ -53,7 +55,13 @@ public enum ContributionType {
*/
public String getTitle() {
String s = toString();
return Character.toUpperCase(s.charAt(0)) + s.substring(1);
if (this == EXAMPLES_PACKAGE)
return Character.toUpperCase(s.charAt(0))
+ s.substring(1, s.indexOf('-') + 1)
+ Character.toUpperCase(s.charAt(s.indexOf('-') + 1))
+ s.substring(s.indexOf('-') + 2);
else
return Character.toUpperCase(s.charAt(0)) + s.substring(1);
}
@@ -65,6 +73,8 @@ public enum ContributionType {
return "tools";
case MODE:
return "modes";
case EXAMPLES_PACKAGE:
return "examples-package";
}
return null; // should be unreachable
}
@@ -106,6 +116,9 @@ public enum ContributionType {
if ("mode".equalsIgnoreCase(s)) {
return MODE;
}
if ("examples-package".equalsIgnoreCase(s)) {
return EXAMPLES_PACKAGE;
}
}
return null;
}
@@ -119,6 +132,8 @@ public enum ContributionType {
return Base.getSketchbookToolsFolder();
case MODE:
return Base.getSketchbookModesFolder();
case EXAMPLES_PACKAGE:
return Base.getSketchbookExamplesPackagesFolder();
}
return null;
}
@@ -136,7 +151,7 @@ public enum ContributionType {
* contribution type. For instance, a list of folders that have a 'mode'
* subfolder if this is a ModeContribution.
*/
File[] listCandidates(File folder) {
public File[] listCandidates(File folder) {
return folder.listFiles(new FileFilter() {
public boolean accept(File potential) {
return isCandidate(potential);
@@ -181,6 +196,8 @@ public enum ContributionType {
return ToolContribution.load(folder);
case MODE:
return ModeContribution.load(base, folder);
case EXAMPLES_PACKAGE:
return ExamplesPackageContribution.load(folder);
}
return null;
}
@@ -198,6 +215,9 @@ public enum ContributionType {
case MODE:
contribs.addAll(editor.getBase().getModeContribs());
break;
case EXAMPLES_PACKAGE:
contribs.addAll(editor.getBase().getExampleContribs());
break;
}
return contribs;
}
@@ -0,0 +1,89 @@
package processing.app.contrib;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import processing.app.Base;
import processing.core.PApplet;
public class ExamplesPackageContribution extends LocalContribution {
private ArrayList<String> compatibleModesList;
static public ExamplesPackageContribution load(File folder) {
return new ExamplesPackageContribution(folder);
}
private ExamplesPackageContribution(File folder) {
super(folder);
compatibleModesList = parseCompatibleModesList(properties
.get("compatibleModesList"));
}
private static ArrayList<String> parseCompatibleModesList(String unparsedModes) {
ArrayList<String> modesList = new ArrayList<String>();
if (unparsedModes == null || unparsedModes.isEmpty())
return modesList;
String[] splitStr = PApplet.trim(PApplet.split(unparsedModes, ','));//unparsedModes.split(",");
for (String mode : splitStr)
modesList.add(mode.trim());
return modesList;
}
/**
* Function to determine whether or not the example present in the
* exampleLocation directory is compatible with the present mode.
*
* @param base
* @param exampleLocationFolder
* @return true if the example is compatible with the mode of the currently
* active editor
*/
public static boolean isExamplesPackageCompatible(Base base,
File exampleLocationFolder) {
File propertiesFile = new File(exampleLocationFolder,
ContributionType.EXAMPLES_PACKAGE.toString()
+ ".properties");
if (propertiesFile.exists()) {
ArrayList<String> compModesList = parseCompatibleModesList(Base
.readSettings(propertiesFile).get("compatibleModesList"));
for (String c : compModesList) {
if (c.equalsIgnoreCase(base.getActiveEditor().getMode().getIdentifier())) {
return true;
}
}
}
return false;
}
static public void loadMissing(Base base) {
File examplesFolder = Base.getSketchbookExamplesPackagesFolder();
ArrayList<ExamplesPackageContribution> contribExamples = base.getExampleContribs();
HashMap<File, ExamplesPackageContribution> existing = new HashMap<File, ExamplesPackageContribution>();
for (ExamplesPackageContribution contrib : contribExamples) {
existing.put(contrib.getFolder(), contrib);
}
File[] potential = ContributionType.EXAMPLES_PACKAGE.listCandidates(examplesFolder);
// If modesFolder does not exist or is inaccessible (folks might like to
// mess with folders then report it as a bug) 'potential' will be null.
if (potential != null) {
for (File folder : potential) {
if (!existing.containsKey(folder)) {
contribExamples.add(new ExamplesPackageContribution(folder));
}
}
}
}
@Override
public ContributionType getType() {
return ContributionType.EXAMPLES_PACKAGE;
}
public ArrayList<String> getCompatibleModesList() {
return compatibleModesList;
}
}