This commit is contained in:
Manindra Moharana
2014-08-19 23:34:37 +05:30
16 changed files with 651 additions and 140 deletions

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 {

View File

@@ -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.

View File

@@ -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();
}
}
});

View File

@@ -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;
}
}

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);

View File

@@ -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();

View File

@@ -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.
*/

View File

@@ -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
// */

View File

@@ -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"));

View File

@@ -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("");

View File

@@ -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) {

View File

@@ -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;
}

View File

@@ -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;
}
}

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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 该库文件地址得到更多信息.
# ---------------------------------------