mirror of
https://github.com/processing/processing4.git
synced 2026-02-13 10:30:44 +01:00
Merge branch 'master' of https://github.com/processing/processing
This commit is contained in:
@@ -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 {
|
||||
|
||||
@@ -2396,35 +2396,42 @@ public abstract class Editor extends JFrame implements RunnerListener {
|
||||
// }
|
||||
}
|
||||
|
||||
//used to prevent the fileChangeListener from asking for reloads after internal changes
|
||||
public void setWatcherSave() {
|
||||
watcherSave = true;
|
||||
}
|
||||
|
||||
//set to true when the sketch is saved from inside processing
|
||||
private boolean watcherSave;
|
||||
private boolean watcherReloaded;
|
||||
|
||||
|
||||
//the key which is being used to poll the fs for changes
|
||||
private WatchKey watcherKey = null;
|
||||
|
||||
private void initFileChangeListener() {
|
||||
try {
|
||||
WatchService watchService = FileSystems.getDefault().newWatchService();
|
||||
Path folderPath = sketch.getFolder().toPath();
|
||||
watcherKey = folderPath.register(watchService,
|
||||
// StandardWatchEventKinds.ENTRY_CREATE,
|
||||
// StandardWatchEventKinds.ENTRY_DELETE,
|
||||
StandardWatchEventKinds.ENTRY_MODIFY);
|
||||
watcherKey = sketch
|
||||
.getFolder()
|
||||
.toPath()
|
||||
.register(watchService, StandardWatchEventKinds.ENTRY_CREATE,
|
||||
StandardWatchEventKinds.ENTRY_DELETE,
|
||||
StandardWatchEventKinds.ENTRY_MODIFY);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
final WatchKey finKey = watcherKey;
|
||||
|
||||
// if the key is null for some reason, don't bother attaching
|
||||
// a listener to it, they can deal without one
|
||||
//if the key is null for some reason, don't bother attaching a listener to it
|
||||
if (finKey != null) {
|
||||
// the key can now be polled for changes in the files
|
||||
addWindowFocusListener(new WindowFocusListener() {
|
||||
@Override
|
||||
public void windowGainedFocus(WindowEvent arg0) {
|
||||
//we switched locations (saveAs), ignore old things
|
||||
if (watcherKey != finKey) {
|
||||
return;
|
||||
}
|
||||
// check preference here for enabled or not?
|
||||
|
||||
//if the directory was deleted, then don't scan
|
||||
@@ -2432,10 +2439,18 @@ public abstract class Editor extends JFrame implements RunnerListener {
|
||||
List<WatchEvent<?>> events = finKey.pollEvents();
|
||||
processFileEvents(events);
|
||||
}
|
||||
|
||||
List<WatchEvent<?>> events = finKey.pollEvents();
|
||||
if (!watcherSave)
|
||||
processFileEvents(events);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void windowLostFocus(WindowEvent arg0){
|
||||
public void windowLostFocus(WindowEvent arg0) {
|
||||
//we switched locations (saveAs), ignore old things
|
||||
if (watcherKey != finKey) {
|
||||
return;
|
||||
}
|
||||
List<WatchEvent<?>> events = finKey.pollEvents();
|
||||
//don't ask to reload a file we saved
|
||||
if (!watcherSave) {
|
||||
@@ -2453,40 +2468,62 @@ public abstract class Editor extends JFrame implements RunnerListener {
|
||||
* @param events the list of events that have occured in the sketch folder
|
||||
*/
|
||||
private void processFileEvents(List<WatchEvent<?>> events) {
|
||||
watcherReloaded = false;
|
||||
for (WatchEvent<?> e : events) {
|
||||
//the context is the name of the file inside the path
|
||||
//due to some weird shit, if a file was editted in gedit, the context is .goutputstream-XXXXX
|
||||
//this makes things.... complicated
|
||||
//System.out.println(e.context());
|
||||
|
||||
//if we already reloaded in this cycle, then don't reload again
|
||||
if (watcherReloaded){
|
||||
break;
|
||||
boolean sketchFile = false;
|
||||
Path file = ((Path) e.context()).getFileName();
|
||||
for (String s : getMode().getExtensions()) {
|
||||
//if it is a change to a file with a known extension
|
||||
if (file.toString().endsWith(s)) {
|
||||
sketchFile = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (e.kind().equals(StandardWatchEventKinds.ENTRY_MODIFY)) {
|
||||
// Path p = (Path) e.context();
|
||||
// Path root = (Path) key.watchable();
|
||||
// Path path = root.resolve(p);
|
||||
int response =
|
||||
Base.showYesNoQuestion(Editor.this, "File Modified",
|
||||
"A file has been modified externally",
|
||||
"Would you like to reload the sketch?");
|
||||
if (response == 0) {
|
||||
// reload the sketch
|
||||
//if the file is not a known type, then go the the next event
|
||||
if (!sketchFile) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int response = Base
|
||||
.showYesNoQuestion(Editor.this,
|
||||
"File Modified",
|
||||
"Your sketch has been modified externally",
|
||||
"Would you like to reload the sketch?");
|
||||
if (response == 0) {
|
||||
//grab the 'main' code in case this reload tries to delete everything
|
||||
File sc = sketch.getMainFile();
|
||||
//reload the sketch
|
||||
try {
|
||||
sketch.reload();
|
||||
header.rebuild();
|
||||
watcherReloaded = true;
|
||||
} catch (Exception f) {
|
||||
if (sketch.getCodeCount() < 1) {
|
||||
Base
|
||||
.showWarning("Canceling Reload",
|
||||
"You cannot delete the last code file in a sketch!");
|
||||
//if they deleted the last file, re-save the SketchCode
|
||||
try {
|
||||
//make a blank file
|
||||
sc.createNewFile();
|
||||
} catch (IOException e1) {
|
||||
//if that didn't work, tell them it's un-recoverable
|
||||
Base.showError("Reload failed",
|
||||
"The sketch contians no code files", e1);
|
||||
//don't try to reload again after the double fail
|
||||
//this editor is probably trashed by this point, but a save-as might be possible
|
||||
break;
|
||||
}
|
||||
//don't ask for another reload after this save
|
||||
watcherSave = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// called when a file is created or deleted
|
||||
// for now, do nothing
|
||||
//now that we've reloaded once, don't try to reload again
|
||||
break;
|
||||
}
|
||||
}
|
||||
watcherSave = false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the title of the PDE window based on the current sketch, i.e.
|
||||
* something like "sketch_070752a - Processing 0126"
|
||||
@@ -2517,7 +2554,7 @@ public abstract class Editor extends JFrame implements RunnerListener {
|
||||
public boolean handleSave(boolean immediately) {
|
||||
// handleStop(); // 0136
|
||||
|
||||
watcherSave = true;
|
||||
setWatcherSave();
|
||||
if (sketch.isUntitled()) {
|
||||
return handleSaveAs();
|
||||
// need to get the name, user might also cancel here
|
||||
@@ -2561,6 +2598,8 @@ public abstract class Editor extends JFrame implements RunnerListener {
|
||||
statusNotice(Language.text("editor.status.saving"));
|
||||
try {
|
||||
if (sketch.saveAs()) {
|
||||
//a saveAs moves where the files are, so a listener must be attached to the new location
|
||||
initFileChangeListener();
|
||||
// statusNotice("Done Saving.");
|
||||
// status is now printed from Sketch so that "Done Saving."
|
||||
// is only printed after Save As when progress bar is shown.
|
||||
|
||||
@@ -512,6 +512,7 @@ public class EditorHeader extends JComponent {
|
||||
item.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
editor.getSketch().handleNewCode();
|
||||
editor.setWatcherSave();
|
||||
}
|
||||
});
|
||||
menu.add(item);
|
||||
@@ -541,6 +542,7 @@ public class EditorHeader extends JComponent {
|
||||
Language.text("editor.header.delete.warning.text"), null);
|
||||
} else {
|
||||
editor.getSketch().handleDeleteCode();
|
||||
editor.setWatcherSave();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -165,8 +165,13 @@ public class Language {
|
||||
|
||||
/** Get translation from bundles. */
|
||||
static public String text(String text) {
|
||||
String result = init().bundle.getString(text);
|
||||
return (result == null) ? text : result;
|
||||
ResourceBundle bundle = init().bundle;
|
||||
|
||||
try {
|
||||
return bundle.getString(text);
|
||||
} catch (MissingResourceException e) {
|
||||
return text;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -30,9 +30,10 @@ import processing.core.PApplet;
|
||||
|
||||
|
||||
abstract public class Contribution {
|
||||
static final String SPECIAL_CATEGORY_NAME = "Starred";
|
||||
static final List validCategories =
|
||||
Arrays.asList("3D", "Animation", "Data", "Geometry", "GUI", "Hardware",
|
||||
"I/O", "Math", "Simulation", "Sound", "Typography",
|
||||
"I/O", "Math", "Simulation", "Sound", SPECIAL_CATEGORY_NAME, "Typography",
|
||||
"Utilities", "Video & Vision", "Other");
|
||||
|
||||
//protected String category; // "Sound"
|
||||
@@ -165,6 +166,19 @@ abstract public class Contribution {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns true if the contribution is a starred/recommended contribution, or
|
||||
* is by the Processing Foundation.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
boolean isSpecial() {
|
||||
if (authorList.indexOf("The Processing Foundation") != -1 || categories.contains(SPECIAL_CATEGORY_NAME))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return a single element list with "Unknown" as the category.
|
||||
*/
|
||||
|
||||
@@ -44,6 +44,7 @@ public class ContributionListing {
|
||||
Map<String, List<Contribution>> librariesByCategory;
|
||||
ArrayList<Contribution> allContributions;
|
||||
boolean hasDownloadedLatestList;
|
||||
boolean hasListDownloadFailed;
|
||||
ReentrantLock downloadingListingLock;
|
||||
|
||||
|
||||
@@ -369,8 +370,11 @@ public class ContributionListing {
|
||||
ContributionManager.download(url, listingFile, progress);
|
||||
if (!progress.isCanceled() && !progress.isError()) {
|
||||
hasDownloadedLatestList = true;
|
||||
hasListDownloadFailed = false;
|
||||
setAdvertisedList(listingFile);
|
||||
}
|
||||
else
|
||||
hasListDownloadFailed = true;
|
||||
}
|
||||
downloadingListingLock.unlock();
|
||||
}
|
||||
@@ -436,6 +440,11 @@ public class ContributionListing {
|
||||
}
|
||||
|
||||
|
||||
boolean hasListDownloadFailed() {
|
||||
return hasListDownloadFailed;
|
||||
}
|
||||
|
||||
|
||||
// /**
|
||||
// * @return a lowercase string with all non-alphabetic characters removed
|
||||
// */
|
||||
|
||||
@@ -102,7 +102,8 @@ public class ContributionManager {
|
||||
} catch (IOException ioe) {
|
||||
if (progress != null)
|
||||
progress.error(ioe);
|
||||
ioe.printStackTrace();
|
||||
// Hiding stack trace. An error has been shown where needed.
|
||||
// ioe.printStackTrace();
|
||||
}
|
||||
if (progress != null)
|
||||
progress.finished();
|
||||
@@ -155,11 +156,26 @@ public class ContributionManager {
|
||||
}
|
||||
installProgress.finished();
|
||||
}
|
||||
else {
|
||||
if (downloadProgress.exception instanceof SocketTimeoutException) {
|
||||
status.setErrorMessage(Language
|
||||
.interpolate("contrib.errors.contrib_download.timeout",
|
||||
ad.getName()));
|
||||
} else {
|
||||
status.setErrorMessage(Language
|
||||
.interpolate("contrib.errors.download_and_install",
|
||||
ad.getName()));
|
||||
}
|
||||
}
|
||||
contribZip.delete();
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
status.setErrorMessage(Language.text("contrib.errors.download_and_install"));
|
||||
// Hiding stack trace. The error message ought to suffice.
|
||||
// e.printStackTrace();
|
||||
status
|
||||
.setErrorMessage(Language
|
||||
.interpolate("contrib.errors.download_and_install",
|
||||
ad.getName()));
|
||||
}
|
||||
} catch (IOException e) {
|
||||
status.setErrorMessage(Language.text("contrib.errors.temporary_directory"));
|
||||
|
||||
@@ -44,6 +44,7 @@ public class ContributionManagerDialog {
|
||||
static final String ANY_CATEGORY = Language.text("contrib.all");
|
||||
|
||||
JFrame dialog;
|
||||
String title;
|
||||
ContributionFilter filter;
|
||||
JComboBox categoryChooser;
|
||||
JScrollPane scrollPane;
|
||||
@@ -51,6 +52,7 @@ public class ContributionManagerDialog {
|
||||
StatusPanel status;
|
||||
FilterField filterField;
|
||||
JButton restartButton;
|
||||
JButton retryConnectingButton;
|
||||
|
||||
// the calling editor, so updates can be applied
|
||||
Editor editor;
|
||||
@@ -60,8 +62,16 @@ public class ContributionManagerDialog {
|
||||
|
||||
public ContributionManagerDialog(ContributionType type) {
|
||||
if (type == null) {
|
||||
title = Language.text("contrib.manager_title.update");
|
||||
filter = ContributionType.createUpdateFilter();
|
||||
} else {
|
||||
if (type == ContributionType.MODE)
|
||||
title = Language.text("contrib.manager_title.mode");
|
||||
else if (type == ContributionType.TOOL)
|
||||
title = Language.text("contrib.manager_title.tool");
|
||||
else if (type == ContributionType.LIBRARY)
|
||||
title = Language.text("contrib.manager_title.library");
|
||||
|
||||
filter = type.createFilter();
|
||||
}
|
||||
contribListing = ContributionListing.getInstance();
|
||||
@@ -83,7 +93,7 @@ public class ContributionManagerDialog {
|
||||
this.editor = editor;
|
||||
|
||||
if (dialog == null) {
|
||||
dialog = new JFrame(Language.text("contrib"));
|
||||
dialog = new JFrame(title);
|
||||
|
||||
restartButton = new JButton(Language.text("contrib.restart"));
|
||||
restartButton.setVisible(false);
|
||||
@@ -97,7 +107,7 @@ public class ContributionManagerDialog {
|
||||
Editor ed = iter.next();
|
||||
if (ed.getSketch().isModified()) {
|
||||
int option = Base
|
||||
.showYesNoQuestion(editor, Language.text("contrib"),
|
||||
.showYesNoQuestion(editor, title,
|
||||
Language.text("contrib.unsaved_changes"),
|
||||
Language.text("contrib.unsaved_changes.prompt"));
|
||||
|
||||
@@ -132,6 +142,16 @@ public class ContributionManagerDialog {
|
||||
|
||||
});
|
||||
|
||||
retryConnectingButton = new JButton("Retry");
|
||||
retryConnectingButton.setVisible(false);
|
||||
retryConnectingButton.addActionListener(new ActionListener() {
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent arg0) {
|
||||
downloadAndUpdateContributionListing();
|
||||
}
|
||||
});
|
||||
|
||||
Toolkit.setIcon(dialog);
|
||||
createComponents();
|
||||
registerDisposeListeners();
|
||||
@@ -146,23 +166,7 @@ public class ContributionManagerDialog {
|
||||
updateContributionListing();
|
||||
|
||||
} else {
|
||||
contribListing.downloadAvailableList(new ProgressMonitor() {
|
||||
|
||||
public void finished() {
|
||||
super.finished();
|
||||
|
||||
updateContributionListing();
|
||||
updateCategoryChooser();
|
||||
if (error) {
|
||||
if (exception instanceof SocketTimeoutException) {
|
||||
status.setErrorMessage(Language.text("contrib.errors.list_download.timeout"));
|
||||
} else {
|
||||
status.setErrorMessage(Language.text("contrib.errors.list_download"));
|
||||
}
|
||||
exception.printStackTrace();
|
||||
}
|
||||
}
|
||||
});
|
||||
downloadAndUpdateContributionListing();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -259,7 +263,11 @@ public class ContributionManagerDialog {
|
||||
statusRestartPane.setOpaque(false);
|
||||
|
||||
statusRestartPane.add(status, BorderLayout.WEST);
|
||||
|
||||
// Adding both of these to EAST shouldn't pose too much of a problem,
|
||||
// since they can never get added together.
|
||||
statusRestartPane.add(restartButton, BorderLayout.EAST);
|
||||
statusRestartPane.add(retryConnectingButton, BorderLayout.EAST);
|
||||
|
||||
pane.add(statusRestartPane, BorderLayout.SOUTH);
|
||||
|
||||
@@ -393,7 +401,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
|
||||
@@ -411,7 +422,38 @@ public class ContributionManagerDialog {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
protected void downloadAndUpdateContributionListing() {
|
||||
status.setMessage("Downloading contribution list...");
|
||||
retryConnectingButton.setEnabled(false);
|
||||
contribListing.downloadAvailableList(new ProgressMonitor() {
|
||||
|
||||
public void finished() {
|
||||
super.finished();
|
||||
|
||||
updateContributionListing();
|
||||
updateCategoryChooser();
|
||||
|
||||
retryConnectingButton.setEnabled(true);
|
||||
|
||||
if (error) {
|
||||
if (exception instanceof SocketTimeoutException) {
|
||||
status.setErrorMessage(Language.text("contrib.errors.list_download.timeout"));
|
||||
} else {
|
||||
status.setErrorMessage(Language.text("contrib.errors.list_download"));
|
||||
}
|
||||
exception.printStackTrace();
|
||||
retryConnectingButton.setVisible(true);
|
||||
}
|
||||
else {
|
||||
status.setMessage("Done.");
|
||||
retryConnectingButton.setVisible(false);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
protected void setFilterText(String filter) {
|
||||
if (filter == null || filter.isEmpty()) {
|
||||
filterField.setText("");
|
||||
|
||||
@@ -42,6 +42,7 @@ import javax.swing.text.html.StyleSheet;
|
||||
|
||||
import processing.app.Base;
|
||||
import processing.app.Editor;
|
||||
import processing.app.Toolkit;
|
||||
import processing.app.Language;
|
||||
|
||||
|
||||
@@ -515,6 +516,16 @@ class ContributionPanel extends JPanel {
|
||||
|
||||
public void setContribution(Contribution contrib) {
|
||||
this.contrib = contrib;
|
||||
|
||||
|
||||
if (contrib.isSpecial()) {
|
||||
ImageIcon processingIcon = new ImageIcon(Toolkit.getLibImage("icons/pde-"
|
||||
+ "48" + ".png"));
|
||||
JLabel iconLabel = new JLabel(processingIcon);
|
||||
iconLabel.setBorder(new EmptyBorder(4, 7, 7, 7));
|
||||
iconLabel.setVerticalAlignment(SwingConstants.TOP);
|
||||
add(iconLabel, BorderLayout.WEST);
|
||||
}
|
||||
|
||||
// StringBuilder nameText = new StringBuilder();
|
||||
// nameText.append("<html><body><b>");
|
||||
@@ -761,8 +772,10 @@ class ContributionPanel extends JPanel {
|
||||
|
||||
if (contrib != null) {
|
||||
updateButton.setVisible((contribListing.hasUpdates(contrib) && !contrib.isUpdateFlagged() && !contrib.isDeletionFlagged()) || isUpdateInProgress);
|
||||
updateButton.setEnabled(!contribListing.hasListDownloadFailed());
|
||||
}
|
||||
installRemoveButton.setVisible(isSelected() || installRemoveButton.getText().equals(Language.text("contrib.remove")) || isUpdateInProgress);
|
||||
installRemoveButton.setEnabled(installRemoveButton.getText().equals(Language.text("contrib.remove")) ||!contribListing.hasListDownloadFailed());
|
||||
reorganizePaneComponents();
|
||||
|
||||
// for (JTextPane textPane : headerPaneSet) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -91,6 +91,19 @@ public abstract class LocalContribution extends Contribution {
|
||||
name = folder.getName();
|
||||
categories = defaultCategory();
|
||||
}
|
||||
|
||||
if (categories.contains(SPECIAL_CATEGORY_NAME))
|
||||
validateSpecial();
|
||||
}
|
||||
|
||||
|
||||
private void validateSpecial() {
|
||||
for (AvailableContribution available : ContributionListing.getInstance().advertisedContributions)
|
||||
if (available.getName().equals(name)) {
|
||||
if (!available.isSpecial())
|
||||
categories.remove(SPECIAL_CATEGORY_NAME);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -260,6 +260,10 @@ editor.status.printing.canceled = Printing canceled.
|
||||
# Contribution Panel
|
||||
|
||||
contrib = Contribution Manager
|
||||
contrib.manager_title.update = Update Manager
|
||||
contrib.manager_title.mode = Mode Manager
|
||||
contrib.manager_title.tool = Tool Manager
|
||||
contrib.manager_title.library = Library Manager
|
||||
contrib.category = Category:
|
||||
contrib.filter_your_search = Filter your search...
|
||||
contrib.restart = Restart Processing
|
||||
@@ -270,15 +274,17 @@ contrib.messages.install_restart = Please restart Processing to finish installin
|
||||
contrib.messages.update_restart = Please restart Processing to finish updating this item.
|
||||
contrib.errors.list_download = Could not download the list of available contributions.
|
||||
contrib.errors.list_download.timeout = Connection timed out while downloading the contribution list.
|
||||
contrib.errors.download_and_install = Error during download and install.
|
||||
contrib.errors.download_and_install = Error during download and install of %s.
|
||||
contrib.errors.description_unavailable = Description unavailable.
|
||||
contrib.errors.malformed_url = "The link fetched from Processing.org is not valid.\nYou can still install this library manually by visiting\nthe library's website.
|
||||
contrib.errors.malformed_url = The link fetched from Processing.org is not valid.\nYou can still install this library manually by visiting\nthe library's website.
|
||||
contrib.errors.needs_repackage = %s needs to be repackaged according to the %s guidelines.
|
||||
contrib.errors.no_contribution_found = Could not find a %s in the downloaded file.
|
||||
contrib.errors.overwriting_properties = Error overwriting .properties file.
|
||||
contrib.errors.install_failed = Install failed.
|
||||
contrib.errors.update_on_restart_failed = Update on restart of %s failed.
|
||||
contrib.errors.temporary_directory = Could not write to temporary directory.
|
||||
contrib.errors.contrib_download.timeout = Connection timed out while downloading %s.
|
||||
contrib.errors.no_internet_connection = You don't seem to be connected to the Internet.
|
||||
contrib.all = All
|
||||
contrib.undo = Undo
|
||||
contrib.remove = Remove
|
||||
|
||||
@@ -19,11 +19,11 @@ menu.file.examples = 范例程序...
|
||||
menu.file.close = 关闭
|
||||
menu.file.save = 保存
|
||||
menu.file.save_as = 另存为...
|
||||
menu.file.export_application = 导出程序...
|
||||
menu.file.export_application = 输出程序...
|
||||
menu.file.page_setup = 页面设置
|
||||
menu.file.print = 打印...
|
||||
menu.file.preferences = 偏好设定...
|
||||
menu.file.quit = Quit
|
||||
menu.file.quit = 退出
|
||||
|
||||
# | File | Edit | Sketch | Library | Tools | Help |
|
||||
# | Edit |
|
||||
@@ -34,7 +34,7 @@ menu.edit.cut = 剪切
|
||||
menu.edit.copy = 复制
|
||||
menu.edit.copy_as_html = 复制为HTML
|
||||
menu.edit.paste = 黏贴
|
||||
menu.edit.select_all = 选择全部
|
||||
menu.edit.select_all = 全部选择
|
||||
menu.edit.auto_format = 自动对齐
|
||||
menu.edit.comment_uncomment = 注释/取消注释
|
||||
menu.edit.increase_indent = 增加缩进量
|
||||
@@ -91,26 +91,26 @@ menu.help.visit.url = http://processing.org/
|
||||
# Basics
|
||||
|
||||
# Buttons
|
||||
prompt.yes = Yes
|
||||
prompt.no = No
|
||||
prompt.cancel = Cancel
|
||||
prompt.ok = OK
|
||||
prompt.browse = Browse
|
||||
prompt.export = Export
|
||||
prompt.yes = 是
|
||||
prompt.no = 否
|
||||
prompt.cancel = 取消
|
||||
prompt.ok = 确认
|
||||
prompt.browse = 浏览
|
||||
prompt.export = 输出
|
||||
|
||||
|
||||
# ---------------------------------------
|
||||
# Frames
|
||||
|
||||
# Open (Frame)
|
||||
open = Open a Processing sketch...
|
||||
open = 打开 Processing 速写本...
|
||||
|
||||
# Save (Frame)
|
||||
save = Save sketch folder as...
|
||||
save.title = Do you want to save changes to this sketch<br> before closing?
|
||||
save.hint = If you don't save, your changes will be lost.
|
||||
save.btn.save = Save
|
||||
save.btn.dont_save = Don't Save
|
||||
save = 保存速写本文件夹为...
|
||||
save.title = 在关闭前你想要保存该速写本更改内容么<br>?
|
||||
save.hint = 如果你没保存, 你所有的更改内容将丢失.
|
||||
save.btn.save = 保存
|
||||
save.btn.dont_save = 不保存
|
||||
|
||||
# Preferences (Frame)
|
||||
preferences = 偏好设置
|
||||
@@ -121,16 +121,16 @@ preferences.sketchbook_location.popup = 速写本位置
|
||||
preferences.language = 语言
|
||||
preferences.editor_and_console_font = 编辑器和控制台字体
|
||||
preferences.editor_and_console_font.tip = \
|
||||
Select the font used in the Editor and the Console.<br>\
|
||||
为编辑器和控制台选择字体.<br>\
|
||||
Only monospaced (fixed-width) fonts may be used,<br>\
|
||||
though the list may be imperfect.
|
||||
preferences.editor_font_size = 编辑器字体大小
|
||||
preferences.console_font_size = 控制台字体大小
|
||||
preferences.background_color = 展示模式时的背景颜色
|
||||
preferences.background_color.tip = \
|
||||
Select the background color used when using Present.<br>\
|
||||
Present is used to present a sketch in full-screen,<br>\
|
||||
accessible from the Sketch menu.
|
||||
选择背景颜色当使用展示模式时.<br>\
|
||||
展示模式通常被用来在全屏模式下展示速写程序,<br>\
|
||||
可从速写本菜单中访问.
|
||||
preferences.use_smooth_text = 在编辑器窗口中使用平滑字体
|
||||
preferences.enable_complex_text_input = 启用复杂字体输入
|
||||
preferences.enable_complex_text_input_example = i.e. Japanese
|
||||
@@ -141,70 +141,70 @@ preferences.trigger_with = Trigger with
|
||||
preferences.cmd_space = space
|
||||
preferences.increase_max_memory = 增加最大内存至
|
||||
preferences.delete_previous_folder_on_export = 当导出时删除先前的文件夹
|
||||
preferences.hide_toolbar_background_image = Hide tab/toolbar background image
|
||||
preferences.check_for_updates_on_startup = Check for updates on startup
|
||||
preferences.hide_toolbar_background_image = 隐藏标签/工具栏背景图片
|
||||
preferences.check_for_updates_on_startup = 在启动时检查有无更新
|
||||
preferences.run_sketches_on_display = Run sketches on display
|
||||
preferences.run_sketches_on_display.tip = \
|
||||
Sets the display where sketches are initially placed.<br>\
|
||||
As usual, if the sketch window is moved, it will re-open<br>\
|
||||
at the same location, however when running in present<br>\
|
||||
(full screen) mode, this display will always be used.
|
||||
preferences.automatically_associate_pde_files = Automatically associate .pde files with Processing
|
||||
preferences.automatically_associate_pde_files = 自动关联 .pde 文件通过 Processing
|
||||
preferences.launch_programs_in = Launch programs in
|
||||
preferences.launch_programs_in.mode = mode
|
||||
preferences.file = More preferences can be edited directly in the file
|
||||
preferences.file.hint = edit only when Processing is not running
|
||||
preferences.launch_programs_in.mode = 模式
|
||||
preferences.file = 更多选项可直接编辑该文件
|
||||
preferences.file.hint = 请在Processing不在运行时编辑该文件
|
||||
|
||||
# Sketchbook Location (Frame)
|
||||
sketchbook_location = Select new sketchbook location
|
||||
sketchbook_location = 选择新速写本位置
|
||||
|
||||
# Sketchbook (Frame)
|
||||
sketchbook = Sketchbook
|
||||
sketchbook.tree = Sketchbook
|
||||
|
||||
# examples (Frame)
|
||||
examples = Examples
|
||||
examples = 范例程序
|
||||
|
||||
# Export (Frame)
|
||||
export = 导出选项
|
||||
export = 输出选项
|
||||
export.platforms = 系统平台
|
||||
export.options = 选项
|
||||
export.options.fullscreen = 全拼 (展示模式)
|
||||
export.options.fullscreen = 全屏 (展示模式)
|
||||
export.options.show_stop_button = 显示停止按钮
|
||||
export.description.line1 = 为导出程序创建双击事件,
|
||||
export.description.line1 = 为输出程序创建双击事件,
|
||||
export.description.line2 = 为所选系统创建独立运行程序.
|
||||
|
||||
# Find (Frame)
|
||||
find = Find
|
||||
find.find = Find:
|
||||
find.replace_with = Replace with:
|
||||
find.ignore_case = Ignore Case
|
||||
find.all_tabs = All Tabs
|
||||
find = 搜索
|
||||
find.find = 搜索:
|
||||
find.replace_with = 替换为:
|
||||
find.ignore_case = 忽略大小写
|
||||
find.all_tabs = 所有标签
|
||||
find.wrap_around = Wrap Around
|
||||
find.btn.replace_all = Replace All
|
||||
find.btn.replace = Replace
|
||||
find.btn.find_and_replace = Find & Replace
|
||||
find.btn.previous = Previous
|
||||
find.btn.find = Find
|
||||
find.btn.replace_all = 全部替换
|
||||
find.btn.replace = 替换
|
||||
find.btn.find_and_replace = 搜索 & 替换
|
||||
find.btn.previous = 上一个
|
||||
find.btn.find = 搜索
|
||||
|
||||
# Find in reference (Frame)
|
||||
find_in_reference = Find in Reference
|
||||
find_in_reference = 在参考文档中搜索
|
||||
|
||||
# Library Manager (Frame)
|
||||
library.category = Category:
|
||||
library.filter_your_search = Filter your search...
|
||||
library.category = 目录:
|
||||
library.filter_your_search = 筛查需找...
|
||||
|
||||
# File (Frame)
|
||||
file = Select an image or other data file to copy to your sketch
|
||||
file = 选择一个图片或其它文件拷贝至你的速写本中
|
||||
|
||||
# Create Font (Frame)
|
||||
create_font = Create Font
|
||||
create_font = 创建字体
|
||||
|
||||
# Color Selector (Frame)
|
||||
color_selector = Color Selector
|
||||
color_selector = 颜色选择
|
||||
|
||||
# Archive Sketch (Frame)
|
||||
archive_sketch = Archive sketch as...
|
||||
archive_sketch = 速写本压缩输出...
|
||||
|
||||
|
||||
# ---------------------------------------
|
||||
@@ -225,11 +225,11 @@ toolbar.add_mode = 添加模式...
|
||||
# Editor
|
||||
|
||||
# [Tab1] [Tab2] [v]
|
||||
editor.header.new_tab = 新建Tab
|
||||
editor.header.new_tab = 新建标签
|
||||
editor.header.rename = 重命名
|
||||
editor.header.delete = 删除
|
||||
editor.header.previous_tab = 前一个Tab
|
||||
editor.header.next_tab = 后一个Tab
|
||||
editor.header.previous_tab = 前一个标签
|
||||
editor.header.next_tab = 后一个标签
|
||||
editor.header.delete.warning.title = Yeah, no.
|
||||
editor.header.delete.warning.text = You can't delete the last tab of the last open sketch.
|
||||
|
||||
@@ -267,7 +267,7 @@ contrib.install = 安装
|
||||
contrib.progress.starting = 开始
|
||||
contrib.progress.downloading = 下载
|
||||
contrib.download_error = 下载时出现问题.
|
||||
contrib.unsupported_operating_system = 你当前的操作系统似乎不被自持. 你应该访问 %s's 该库文件地址得到更多信息.
|
||||
contrib.unsupported_operating_system = 你当前的操作系统似乎不被支持. 你应该访问 %s's 该库文件地址得到更多信息.
|
||||
|
||||
|
||||
# ---------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user