mirror of
https://github.com/processing/processing4.git
synced 2026-02-12 18:10:43 +01:00
1424 lines
44 KiB
Java
1424 lines
44 KiB
Java
/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
|
|
|
|
/*
|
|
Part of the Processing project - http://processing.org
|
|
|
|
Copyright (c) 2013 The Processing Foundation
|
|
Copyright (c) 2010-13 Ben Fry and Casey Reas
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software Foundation,
|
|
Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
package processing.app;
|
|
|
|
import java.awt.*;
|
|
import java.awt.event.*;
|
|
import java.awt.image.BufferedImage;
|
|
import java.awt.image.WritableRaster;
|
|
import java.io.*;
|
|
import java.util.*;
|
|
import java.util.List;
|
|
|
|
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.tree.*;
|
|
|
|
import processing.app.contrib.ContributionType;
|
|
import processing.app.contrib.ExamplesContribution;
|
|
import processing.app.syntax.*;
|
|
import processing.app.ui.Editor;
|
|
import processing.app.ui.EditorState;
|
|
import processing.app.ui.Toolkit;
|
|
import processing.core.PApplet;
|
|
import processing.core.PConstants;
|
|
|
|
|
|
public abstract class Mode {
|
|
protected Base base;
|
|
|
|
protected File folder;
|
|
|
|
protected TokenMarker tokenMarker;
|
|
protected HashMap<String, String> keywordToReference =
|
|
new HashMap<String, String>();
|
|
|
|
protected Settings theme;
|
|
// protected Formatter formatter;
|
|
// protected Tool formatter;
|
|
|
|
// maps imported packages to their library folder
|
|
// protected HashMap<String, Library> importToLibraryTable;
|
|
protected HashMap<String, ArrayList<Library>> importToLibraryTable;
|
|
|
|
// these menus are shared so that they needn't be rebuilt for all windows
|
|
// each time a sketch is created, renamed, or moved.
|
|
protected JMenu examplesMenu; // this is for the menubar, not the toolbar
|
|
protected JMenu importMenu;
|
|
|
|
// protected JTree examplesTree;
|
|
protected JFrame examplesFrame;
|
|
|
|
// popup menu used for the toolbar
|
|
protected JMenu toolbarMenu;
|
|
|
|
protected File examplesFolder;
|
|
protected File librariesFolder;
|
|
protected File referenceFolder;
|
|
|
|
protected File examplesContribFolder;
|
|
|
|
public List<Library> coreLibraries;
|
|
public List<Library> contribLibraries;
|
|
|
|
/** Library folder for core. (Used for OpenGL in particular.) */
|
|
protected Library coreLibrary;
|
|
|
|
/**
|
|
* ClassLoader used to retrieve classes for this mode. Useful if you want
|
|
* to grab any additional classes that subclass what's in the mode folder.
|
|
*/
|
|
protected ClassLoader classLoader;
|
|
|
|
static final int BACKGROUND_WIDTH = 1025;
|
|
static final int BACKGROUND_HEIGHT = 65;
|
|
protected Image backgroundImage;
|
|
|
|
// public Mode(Base base, File folder) {
|
|
// this(base, folder, base.getSketchbookLibrariesFolder());
|
|
// }
|
|
|
|
|
|
public Mode(Base base, File folder) {
|
|
this.base = base;
|
|
this.folder = folder;
|
|
tokenMarker = createTokenMarker();
|
|
|
|
// Get paths for the libraries and examples in the mode folder
|
|
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.getSketchbookExamplesFolder();
|
|
|
|
// rebuildToolbarMenu();
|
|
rebuildLibraryList();
|
|
// rebuildExamplesMenu();
|
|
|
|
try {
|
|
for (File file : getKeywordFiles()) {
|
|
loadKeywords(file);
|
|
}
|
|
} catch (IOException e) {
|
|
Base.showWarning("Problem loading keywords",
|
|
"Could not load keywords file for " + getTitle() + " mode.", e);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* To add additional keywords, or to grab them from another mode, override
|
|
* this function. If your mode has no keywords, return a zero length array.
|
|
*/
|
|
public File[] getKeywordFiles() {
|
|
return new File[] { new File(folder, "keywords.txt") };
|
|
}
|
|
|
|
|
|
protected void loadKeywords(File keywordFile) throws IOException {
|
|
// overridden for Python, where # is an actual keyword
|
|
loadKeywords(keywordFile, "#");
|
|
}
|
|
|
|
|
|
protected void loadKeywords(File keywordFile,
|
|
String commentPrefix) throws IOException {
|
|
BufferedReader reader = PApplet.createReader(keywordFile);
|
|
String line = null;
|
|
while ((line = reader.readLine()) != null) {
|
|
if (!line.trim().startsWith(commentPrefix)) {
|
|
// Was difficult to make sure that mode authors were properly doing
|
|
// tab-separated values. By definition, there can't be additional
|
|
// spaces inside a keyword (or filename), so just splitting on tokens.
|
|
String[] pieces = PApplet.splitTokens(line);
|
|
if (pieces.length >= 2) {
|
|
String keyword = pieces[0];
|
|
String coloring = pieces[1];
|
|
|
|
if (coloring.length() > 0) {
|
|
tokenMarker.addColoring(keyword, coloring);
|
|
}
|
|
if (pieces.length == 3) {
|
|
String htmlFilename = pieces[2];
|
|
if (htmlFilename.length() > 0) {
|
|
// if the file is for the version with parens,
|
|
// add a paren to the keyword
|
|
if (htmlFilename.endsWith("_")) {
|
|
keyword += "_";
|
|
}
|
|
keywordToReference.put(keyword, htmlFilename);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
reader.close();
|
|
}
|
|
|
|
|
|
public void setClassLoader(ClassLoader loader) {
|
|
this.classLoader = loader;
|
|
}
|
|
|
|
|
|
public ClassLoader getClassLoader() {
|
|
return classLoader;
|
|
}
|
|
|
|
|
|
/**
|
|
* Setup additional elements that are only required when running with a GUI,
|
|
* rather than from the command-line. Note that this will not be called when
|
|
* the Mode is used from the command line (because Base will be null).
|
|
*/
|
|
public void setupGUI() {
|
|
try {
|
|
// First load the default theme data for the whole PDE.
|
|
theme = new Settings(Base.getContentFile("lib/theme.txt"));
|
|
|
|
// The mode-specific theme.txt file should only contain additions,
|
|
// and in extremely rare cases, it might override entries from the
|
|
// main theme. Do not override for style changes unless they are
|
|
// objectively necessary for your Mode.
|
|
File modeTheme = new File(folder, "theme/theme.txt");
|
|
if (modeTheme.exists()) {
|
|
// Override the built-in settings with what the theme provides
|
|
theme.load(modeTheme);
|
|
}
|
|
|
|
// other things that have to be set explicitly for the defaults
|
|
theme.setColor("run.window.bgcolor", SystemColor.control);
|
|
|
|
// loadBackground();
|
|
|
|
} catch (IOException e) {
|
|
Base.showError("Problem loading theme.txt",
|
|
"Could not load theme.txt, please re-install Processing", e);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
protected void loadBackground() {
|
|
String suffix = Toolkit.highResDisplay() ? "-2x.png" : ".png";
|
|
backgroundImage = loadImage("theme/mode" + suffix);
|
|
if (backgroundImage == null) {
|
|
// If the image wasn't available, try the other resolution.
|
|
// i.e. we don't (currently) have low-res versions of mode.png,
|
|
// so this will grab the 2x version and scale it when drawn.
|
|
suffix = !Toolkit.highResDisplay() ? "-2x.png" : ".png";
|
|
backgroundImage = loadImage("theme/mode" + suffix);
|
|
}
|
|
}
|
|
|
|
|
|
public void drawBackground(Graphics g, int offset) {
|
|
if (backgroundImage != null) {
|
|
if (!Toolkit.highResDisplay()) {
|
|
// Image might be downsampled from a 2x version. If so, we need nice
|
|
// anti-aliasing for the very geometric images we're using.
|
|
Graphics2D g2 = (Graphics2D) g;
|
|
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
|
|
RenderingHints.VALUE_ANTIALIAS_ON);
|
|
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
|
|
RenderingHints.VALUE_INTERPOLATION_BICUBIC);
|
|
}
|
|
g.drawImage(backgroundImage, 0, -offset,
|
|
BACKGROUND_WIDTH, BACKGROUND_HEIGHT, null);
|
|
}
|
|
}
|
|
*/
|
|
|
|
|
|
public File getContentFile(String path) {
|
|
return new File(folder, path);
|
|
}
|
|
|
|
|
|
public InputStream getContentStream(String path) throws FileNotFoundException {
|
|
return new FileInputStream(getContentFile(path));
|
|
}
|
|
|
|
|
|
/**
|
|
* Return the pretty/printable/menu name for this mode. This is separate from
|
|
* the single word name of the folder that contains this mode. It could even
|
|
* have spaces, though that might result in sheer madness or total mayhem.
|
|
*/
|
|
abstract public String getTitle();
|
|
|
|
|
|
/**
|
|
* Get an identifier that can be used to resurrect this mode and connect it
|
|
* to a sketch. Using this instead of getTitle() because there might be name
|
|
* clashes with the titles, but there should not be once the actual package,
|
|
* et al. is included.
|
|
* @return full name (package + class name) for this mode.
|
|
*/
|
|
public String getIdentifier() {
|
|
return getClass().getCanonicalName();
|
|
}
|
|
|
|
|
|
/**
|
|
* Create a new editor associated with this mode.
|
|
*/
|
|
abstract public Editor createEditor(Base base, String path, EditorState state);
|
|
//abstract public Editor createEditor(Base base, String path, int[] location);
|
|
|
|
|
|
/**
|
|
* Get the folder where this mode is stored.
|
|
* @since 3.0a3
|
|
*/
|
|
public File getFolder() {
|
|
return folder;
|
|
}
|
|
|
|
|
|
public File getExamplesFolder() {
|
|
return examplesFolder;
|
|
}
|
|
|
|
|
|
public File getLibrariesFolder() {
|
|
return librariesFolder;
|
|
}
|
|
|
|
|
|
public File getReferenceFolder() {
|
|
return referenceFolder;
|
|
}
|
|
|
|
|
|
public void rebuildLibraryList() {
|
|
//new Exception("Rebuilding library list").printStackTrace(System.out);
|
|
// reset the table mapping imports to libraries
|
|
importToLibraryTable = new HashMap<String, ArrayList<Library>>();
|
|
|
|
coreLibraries = Library.list(librariesFolder);
|
|
for (Library lib : coreLibraries) {
|
|
lib.addPackageList(importToLibraryTable);
|
|
}
|
|
|
|
File contribLibrariesFolder = Base.getSketchbookLibrariesFolder();
|
|
if (contribLibrariesFolder != null) {
|
|
contribLibraries = Library.list(contribLibrariesFolder);
|
|
for (Library lib : contribLibraries) {
|
|
lib.addPackageList(importToLibraryTable);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
public Library getCoreLibrary() {
|
|
return null;
|
|
}
|
|
|
|
|
|
public Library getLibrary(String pkgName) throws SketchException {
|
|
ArrayList<Library> libraries = importToLibraryTable.get(pkgName);
|
|
if (libraries == null) {
|
|
return null;
|
|
|
|
} else if (libraries.size() > 1) {
|
|
String primary = "More than one library is competing for this sketch.";
|
|
String secondary = "The import " + pkgName + " points to multiple libraries:<br>";
|
|
for (Library library : libraries) {
|
|
String location = library.getPath();
|
|
if (location.startsWith(getLibrariesFolder().getAbsolutePath())) {
|
|
location = "part of Processing";
|
|
}
|
|
secondary += "<b>" + library.getName() + "</b> (" + location + ")<br>";
|
|
}
|
|
secondary += "Extra libraries need to be removed before this sketch can be used.";
|
|
Base.showWarningTiered("Duplicate Library Problem", primary, secondary, null);
|
|
throw new SketchException("Duplicate libraries found for " + pkgName + ".");
|
|
|
|
} else {
|
|
return libraries.get(0);
|
|
}
|
|
}
|
|
|
|
|
|
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
|
|
|
|
|
|
// abstract public EditorToolbar createToolbar(Editor editor);
|
|
|
|
|
|
public JMenu getToolbarMenu() {
|
|
if (toolbarMenu == null) {
|
|
// toolbarMenu = new JMenu();
|
|
rebuildToolbarMenu();
|
|
}
|
|
return toolbarMenu;
|
|
}
|
|
|
|
|
|
public void insertToolbarRecentMenu() {
|
|
if (toolbarMenu == null) {
|
|
rebuildToolbarMenu();
|
|
} else {
|
|
toolbarMenu.insert(base.getToolbarRecentMenu(), 1);
|
|
}
|
|
}
|
|
|
|
|
|
public void removeToolbarRecentMenu() {
|
|
toolbarMenu.remove(base.getToolbarRecentMenu());
|
|
}
|
|
|
|
|
|
protected void rebuildToolbarMenu() { //JMenu menu) {
|
|
JMenuItem item;
|
|
if (toolbarMenu == null) {
|
|
toolbarMenu = new JMenu();
|
|
} else {
|
|
toolbarMenu.removeAll();
|
|
}
|
|
|
|
//System.out.println("rebuilding toolbar menu");
|
|
// Add the single "Open" item
|
|
item = Toolkit.newJMenuItem("Open...", 'O');
|
|
item.addActionListener(new ActionListener() {
|
|
public void actionPerformed(ActionEvent e) {
|
|
base.handleOpenPrompt();
|
|
}
|
|
});
|
|
toolbarMenu.add(item);
|
|
|
|
insertToolbarRecentMenu();
|
|
|
|
item = Toolkit.newJMenuItemShift("Examples...", 'O');
|
|
item.addActionListener(new ActionListener() {
|
|
public void actionPerformed(ActionEvent e) {
|
|
showExamplesFrame();
|
|
}
|
|
});
|
|
toolbarMenu.add(item);
|
|
|
|
item = new JMenuItem(Language.text("examples.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();
|
|
base.populateSketchbookMenu(toolbarMenu);
|
|
// boolean found = false;
|
|
// try {
|
|
// found = base.addSketches(toolbarMenu, base.getSketchbookFolder(), true);
|
|
// } catch (IOException e) {
|
|
// Base.showWarning("Sketchbook Toolbar Error",
|
|
// "An error occurred while trying to list the sketchbook.", e);
|
|
// }
|
|
// if (!found) {
|
|
// JMenuItem empty = new JMenuItem("(empty)");
|
|
// empty.setEnabled(false);
|
|
// toolbarMenu.add(empty);
|
|
// }
|
|
}
|
|
|
|
|
|
protected int importMenuIndex = -1;
|
|
|
|
/**
|
|
* Rather than re-building the library menu for every open sketch (very slow
|
|
* and prone to bugs when updating libs, particularly with the contribs mgr),
|
|
* share a single instance across all windows.
|
|
* @since 3.0a6
|
|
* @param sketchMenu the Sketch menu that's currently active
|
|
*/
|
|
public void removeImportMenu(JMenu sketchMenu) {
|
|
JMenu importMenu = getImportMenu();
|
|
//importMenuIndex = sketchMenu.getComponentZOrder(importMenu);
|
|
importMenuIndex = Toolkit.getMenuItemIndex(sketchMenu, importMenu);
|
|
sketchMenu.remove(importMenu);
|
|
}
|
|
|
|
|
|
/**
|
|
* Re-insert the Import Library menu. Added function so that other modes
|
|
* need not have an 'import' menu.
|
|
* @since 3.0a6
|
|
* @param sketchMenu the Sketch menu that's currently active
|
|
*/
|
|
public void insertImportMenu(JMenu sketchMenu) {
|
|
// hard-coded as 4 in 3.0a5, change to 5 for 3.0a6, but... yuck
|
|
//sketchMenu.insert(mode.getImportMenu(), 4);
|
|
// This is -1 on when the editor window is first shown, but that's fine
|
|
// because the import menu has just been added in the Editor constructor.
|
|
if (importMenuIndex != -1) {
|
|
sketchMenu.insert(getImportMenu(), importMenuIndex);
|
|
}
|
|
}
|
|
|
|
|
|
public JMenu getImportMenu() {
|
|
if (importMenu == null) {
|
|
rebuildImportMenu();
|
|
}
|
|
return importMenu;
|
|
}
|
|
|
|
|
|
public void rebuildImportMenu() { //JMenu importMenu) {
|
|
if (importMenu == null) {
|
|
importMenu = new JMenu(Language.text("menu.library"));
|
|
} else {
|
|
//System.out.println("rebuilding import menu");
|
|
importMenu.removeAll();
|
|
}
|
|
|
|
JMenuItem addLib = new JMenuItem(Language.text("menu.library.add_library"));
|
|
addLib.addActionListener(new ActionListener() {
|
|
public void actionPerformed(ActionEvent e) {
|
|
base.handleOpenLibraryManager();
|
|
}
|
|
});
|
|
importMenu.add(addLib);
|
|
importMenu.addSeparator();
|
|
|
|
rebuildLibraryList();
|
|
|
|
ActionListener listener = new ActionListener() {
|
|
public void actionPerformed(ActionEvent e) {
|
|
base.activeEditor.handleImportLibrary(e.getActionCommand());
|
|
}
|
|
};
|
|
|
|
// try {
|
|
// pw = new PrintWriter(new FileWriter(System.getProperty("user.home") + "/Desktop/libs.csv"));
|
|
// } catch (IOException e1) {
|
|
// e1.printStackTrace();
|
|
// }
|
|
|
|
if (coreLibraries.size() == 0) {
|
|
JMenuItem item = new JMenuItem(getTitle() + " " + Language.text("menu.library.no_core_libraries"));
|
|
item.setEnabled(false);
|
|
importMenu.add(item);
|
|
} else {
|
|
for (Library library : coreLibraries) {
|
|
JMenuItem item = new JMenuItem(library.getName());
|
|
item.addActionListener(listener);
|
|
|
|
// changed to library-name to facilitate specification of imports from properties file
|
|
item.setActionCommand(library.getName());
|
|
|
|
importMenu.add(item);
|
|
}
|
|
}
|
|
|
|
if (contribLibraries.size() != 0) {
|
|
importMenu.addSeparator();
|
|
JMenuItem contrib = new JMenuItem(Language.text("menu.library.contributed"));
|
|
contrib.setEnabled(false);
|
|
importMenu.add(contrib);
|
|
|
|
HashMap<String, JMenu> subfolders = new HashMap<String, JMenu>();
|
|
|
|
for (Library library : contribLibraries) {
|
|
JMenuItem item = new JMenuItem(library.getName());
|
|
item.addActionListener(listener);
|
|
|
|
// changed to library-name to facilitate specification if imports from properties file
|
|
item.setActionCommand(library.getName());
|
|
|
|
String group = library.getGroup();
|
|
if (group != null) {
|
|
JMenu subMenu = subfolders.get(group);
|
|
if (subMenu == null) {
|
|
subMenu = new JMenu(group);
|
|
importMenu.add(subMenu);
|
|
subfolders.put(group, subMenu);
|
|
}
|
|
subMenu.add(item);
|
|
} else {
|
|
importMenu.add(item);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
public JMenu getExamplesMenu() {
|
|
if (examplesMenu == null) {
|
|
rebuildExamplesMenu();
|
|
}
|
|
return examplesMenu;
|
|
}
|
|
|
|
|
|
public void rebuildExamplesMenu() {
|
|
if (examplesMenu == null) {
|
|
examplesMenu = new JMenu("Examples");
|
|
}
|
|
rebuildExamplesMenu(examplesMenu, false);
|
|
}
|
|
|
|
|
|
public void rebuildExamplesMenu(JMenu menu, boolean replace) {
|
|
try {
|
|
// break down the examples folder for examples
|
|
File[] subfolders = getExampleCategoryFolders();
|
|
|
|
for (File sub : subfolders) {
|
|
Base.addDisabledItem(menu, sub.getName());
|
|
// JMenuItem categoryItem = new JMenuItem(sub.getName());
|
|
// categoryItem.setEnabled(false);
|
|
// menu.add(categoryItem);
|
|
base.addSketches(menu, sub, replace);
|
|
menu.addSeparator();
|
|
}
|
|
|
|
// if (coreLibraries == null) {
|
|
// rebuildLibraryList();
|
|
// }
|
|
|
|
// get library examples
|
|
Base.addDisabledItem(menu, "Libraries");
|
|
for (Library lib : coreLibraries) {
|
|
if (lib.hasExamples()) {
|
|
JMenu libMenu = new JMenu(lib.getName());
|
|
base.addSketches(libMenu, lib.getExamplesFolder(), replace);
|
|
menu.add(libMenu);
|
|
}
|
|
}
|
|
|
|
// get contrib library examples
|
|
boolean any = false;
|
|
for (Library lib : contribLibraries) {
|
|
if (lib.hasExamples()) {
|
|
any = true;
|
|
}
|
|
}
|
|
if (any) {
|
|
menu.addSeparator();
|
|
Base.addDisabledItem(menu, "Contributed");
|
|
for (Library lib : contribLibraries) {
|
|
if (lib.hasExamples()) {
|
|
JMenu libMenu = new JMenu(lib.getName());
|
|
base.addSketches(libMenu, lib.getExamplesFolder(), replace);
|
|
menu.add(libMenu);
|
|
}
|
|
}
|
|
}
|
|
} catch (IOException e) {
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
*/
|
|
|
|
|
|
/**
|
|
* Override this to control the order of the first set of example folders
|
|
* and how they appear in the examples window.
|
|
*/
|
|
public File[] getExampleCategoryFolders() {
|
|
return examplesFolder.listFiles(new FilenameFilter() {
|
|
public boolean accept(File dir, String name) {
|
|
return dir.isDirectory() && name.charAt(0) != '.';
|
|
}
|
|
});
|
|
}
|
|
|
|
|
|
public DefaultMutableTreeNode buildExamplesTree() {
|
|
DefaultMutableTreeNode root = new DefaultMutableTreeNode("Examples");
|
|
|
|
try {
|
|
|
|
File[] examples = getExampleCategoryFolders();
|
|
|
|
for (File subFolder : examples) {
|
|
DefaultMutableTreeNode subNode = new DefaultMutableTreeNode(subFolder.getName());
|
|
if (base.addSketches(subNode, subFolder)) {
|
|
root.add(subNode);
|
|
}
|
|
}
|
|
|
|
DefaultMutableTreeNode foundationLibraries =
|
|
new DefaultMutableTreeNode(Language.text("examples.core_libraries"));
|
|
|
|
// Get examples for core libraries
|
|
for (Library lib : coreLibraries) {
|
|
if (lib.hasExamples()) {
|
|
DefaultMutableTreeNode libNode = new DefaultMutableTreeNode(lib.getName());
|
|
if (base.addSketches(libNode, lib.getExamplesFolder()))
|
|
foundationLibraries.add(libNode);
|
|
}
|
|
}
|
|
if(foundationLibraries.getChildCount() > 0) {
|
|
root.add(foundationLibraries);
|
|
}
|
|
|
|
// Get examples for third party libraries
|
|
DefaultMutableTreeNode contributedLibExamples = new
|
|
DefaultMutableTreeNode(Language.text("examples.libraries"));
|
|
for (Library lib : contribLibraries) {
|
|
if (lib.hasExamples()) {
|
|
DefaultMutableTreeNode libNode =
|
|
new DefaultMutableTreeNode(lib.getName());
|
|
base.addSketches(libNode, lib.getExamplesFolder());
|
|
contributedLibExamples.add(libNode);
|
|
}
|
|
}
|
|
if(contributedLibExamples.getChildCount() > 0){
|
|
root.add(contributedLibExamples);
|
|
}
|
|
} catch (IOException e) {
|
|
e.printStackTrace();
|
|
}
|
|
|
|
DefaultMutableTreeNode contributedExamplesNode =
|
|
buildContributedExamplesTrees();
|
|
if(contributedExamplesNode.getChildCount() > 0){
|
|
root.add(contributedExamplesNode);
|
|
}
|
|
|
|
return root;
|
|
}
|
|
|
|
|
|
private DefaultMutableTreeNode buildContributedExamplesTrees() {
|
|
DefaultMutableTreeNode contribExamplesNode =
|
|
new DefaultMutableTreeNode(Language.text("examples.contributed"));
|
|
|
|
try {
|
|
File[] subfolders =
|
|
ContributionType.EXAMPLES.listCandidates(examplesContribFolder);
|
|
if (subfolders == null) {
|
|
subfolders = new File[0]; //empty array
|
|
}
|
|
for (File sub : subfolders) {
|
|
if (!ExamplesContribution.isCompatible(base, sub))
|
|
continue;
|
|
DefaultMutableTreeNode subNode =
|
|
new DefaultMutableTreeNode(sub.getName());
|
|
if (base.addSketches(subNode, sub)) {
|
|
contribExamplesNode.add(subNode);
|
|
int exampleNodeNumber = -1;
|
|
for (int y = 0; y < subNode.getChildCount(); y++)
|
|
if (subNode.getChildAt(y).toString().equals("examples"))
|
|
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 contribExamplesNode;
|
|
}
|
|
|
|
|
|
public void rebuildExamplesFrame() {
|
|
if (examplesFrame != null) {
|
|
boolean visible = examplesFrame.isVisible();
|
|
Rectangle bounds = null;
|
|
if (visible) {
|
|
bounds = examplesFrame.getBounds();
|
|
examplesFrame.setVisible(false);
|
|
}
|
|
examplesFrame = null;
|
|
if (visible) {
|
|
showExamplesFrame();
|
|
examplesFrame.setBounds(bounds);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
public void showExamplesFrame() {
|
|
if (examplesFrame == null) {
|
|
examplesFrame = new JFrame(getTitle() + " " + Language.text("examples"));
|
|
Toolkit.setIcon(examplesFrame);
|
|
Toolkit.registerWindowCloseKeys(examplesFrame.getRootPane(), new ActionListener() {
|
|
public void actionPerformed(ActionEvent e) {
|
|
examplesFrame.setVisible(false);
|
|
}
|
|
});
|
|
|
|
JPanel examplesPanel = new JPanel();
|
|
examplesPanel.setLayout(new BorderLayout());
|
|
examplesPanel.setBackground(Color.WHITE);
|
|
|
|
final JPanel openExamplesManagerPanel = new JPanel(new FlowLayout(FlowLayout.CENTER));
|
|
JButton addExamplesButton = new JButton(Language.text("examples.add_examples"));
|
|
openExamplesManagerPanel.add(addExamplesButton);
|
|
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));
|
|
openExamplesManagerPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
|
|
openExamplesManagerPanel.setCursor(new Cursor(Cursor.HAND_CURSOR));
|
|
addExamplesButton.addActionListener(new ActionListener() {
|
|
@Override
|
|
public void actionPerformed(ActionEvent e) {
|
|
base.handleOpenExampleManager();
|
|
}
|
|
});
|
|
|
|
final JTree tree = new JTree(buildExamplesTree());
|
|
|
|
tree.setOpaque(true);
|
|
tree.setAlignmentX(Component.LEFT_ALIGNMENT);
|
|
|
|
tree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
|
|
tree.setShowsRootHandles(true);
|
|
// expand the root
|
|
tree.expandRow(0);
|
|
// now hide the root
|
|
tree.setRootVisible(false);
|
|
|
|
// After 2.0a7, no longer expanding each of the categories at Casey's
|
|
// request. He felt that the window was too complicated too quickly.
|
|
// for (int row = tree.getRowCount()-1; row >= 0; --row) {
|
|
// tree.expandRow(row);
|
|
// }
|
|
|
|
tree.addMouseListener(new MouseAdapter() {
|
|
public void mouseClicked(MouseEvent e) {
|
|
if (e.getClickCount() == 2) {
|
|
DefaultMutableTreeNode node =
|
|
(DefaultMutableTreeNode) tree.getLastSelectedPathComponent();
|
|
|
|
int selRow = tree.getRowForLocation(e.getX(), e.getY());
|
|
//TreePath selPath = tree.getPathForLocation(e.getX(), e.getY());
|
|
//if (node != null && node.isLeaf() && node.getPath().equals(selPath)) {
|
|
if (node != null && node.isLeaf() && selRow != -1) {
|
|
SketchReference sketch = (SketchReference) node.getUserObject();
|
|
base.handleOpen(sketch.getPath());
|
|
}
|
|
}
|
|
}
|
|
});
|
|
tree.addKeyListener(new KeyAdapter() {
|
|
public void keyPressed(KeyEvent e) {
|
|
if (e.getKeyCode() == KeyEvent.VK_ESCAPE) { // doesn't fire keyTyped()
|
|
examplesFrame.setVisible(false);
|
|
}
|
|
}
|
|
public void keyTyped(KeyEvent e) {
|
|
if (e.getKeyChar() == KeyEvent.VK_ENTER) {
|
|
DefaultMutableTreeNode node =
|
|
(DefaultMutableTreeNode) tree.getLastSelectedPathComponent();
|
|
if (node != null && node.isLeaf()) {
|
|
SketchReference sketch = (SketchReference) node.getUserObject();
|
|
base.handleOpen(sketch.getPath());
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
tree.addTreeExpansionListener(new TreeExpansionListener() {
|
|
@Override
|
|
public void treeExpanded(TreeExpansionEvent event) {
|
|
updateExpanded(tree);
|
|
}
|
|
|
|
@Override
|
|
public void treeCollapsed(TreeExpansionEvent event) {
|
|
updateExpanded(tree);
|
|
}
|
|
});
|
|
|
|
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, 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);
|
|
}
|
|
|
|
// Space for the editor plus a li'l gap
|
|
int roughWidth = examplesFrame.getWidth() + 20;
|
|
Point p = null;
|
|
// If no window open, or the editor is at the edge of the screen
|
|
if (base.activeEditor == null ||
|
|
(p = base.activeEditor.getLocation()).x < roughWidth) {
|
|
// Center the window on the screen
|
|
examplesFrame.setLocationRelativeTo(null);
|
|
} else {
|
|
// Open the window relative to the editor
|
|
examplesFrame.setLocation(p.x - roughWidth, p.y);
|
|
}
|
|
examplesFrame.setVisible(true);
|
|
}
|
|
|
|
|
|
protected void updateExpanded(JTree tree) {
|
|
Enumeration en = tree.getExpandedDescendants(new TreePath(tree.getModel().getRoot()));
|
|
//en.nextElement(); // skip the root "Examples" node
|
|
|
|
StringBuilder s = new StringBuilder();
|
|
while (en.hasMoreElements()) {
|
|
//System.out.println(en.nextElement());
|
|
TreePath tp = (TreePath) en.nextElement();
|
|
Object[] path = tp.getPath();
|
|
for (Object o : path) {
|
|
DefaultMutableTreeNode p = (DefaultMutableTreeNode) o;
|
|
String name = (String) p.getUserObject();
|
|
//System.out.print(p.getUserObject().getClass().getName() + ":" + p.getUserObject() + " -> ");
|
|
//System.out.print(name + " -> ");
|
|
s.append(name);
|
|
s.append(File.separatorChar);
|
|
}
|
|
//System.out.println();
|
|
s.setCharAt(s.length() - 1, File.pathSeparatorChar);
|
|
}
|
|
s.setLength(s.length() - 1); // nix that last separator
|
|
String pref = "examples." + getClass().getName() + ".visible";
|
|
Preferences.set(pref, s.toString());
|
|
Preferences.save();
|
|
// System.out.println(s);
|
|
// System.out.println();
|
|
}
|
|
|
|
|
|
protected void restoreExpanded(JTree tree) {
|
|
String pref = "examples." + getClass().getName() + ".visible";
|
|
String value = Preferences.get(pref);
|
|
if (value != null) {
|
|
String[] paths = PApplet.split(value, File.pathSeparator);
|
|
for (String path : paths) {
|
|
// System.out.println("trying to expand " + path);
|
|
String[] items = PApplet.split(path, File.separator);
|
|
DefaultMutableTreeNode[] nodes = new DefaultMutableTreeNode[items.length];
|
|
expandTree(tree, null, items, nodes, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void expandTree(JTree tree, Object object, String[] items, DefaultMutableTreeNode[] nodes, int index) {
|
|
// if (object == null) {
|
|
// object = model.getRoot();
|
|
// }
|
|
TreeModel model = tree.getModel();
|
|
|
|
if (index == 0) {
|
|
nodes[0] = (DefaultMutableTreeNode) model.getRoot();
|
|
expandTree(tree, nodes[0], items, nodes, 1);
|
|
|
|
} else if (index < items.length) {
|
|
// String item = items[0];
|
|
// TreeModel model = object.getModel();
|
|
// System.out.println(object.getClass().getName());
|
|
DefaultMutableTreeNode node = (DefaultMutableTreeNode) object;
|
|
int count = model.getChildCount(node);
|
|
// System.out.println("child count is " + count);
|
|
for (int i = 0; i < count; i++) {
|
|
DefaultMutableTreeNode child = (DefaultMutableTreeNode) model.getChild(node, i);
|
|
if (items[index].equals(child.getUserObject())) {
|
|
nodes[index] = child;
|
|
expandTree(tree, child, items, nodes, index+1);
|
|
}
|
|
}
|
|
} else { // last one
|
|
// PApplet.println(nodes);
|
|
tree.expandPath(new TreePath(nodes));
|
|
}
|
|
}
|
|
|
|
|
|
// protected TreePath findPath(FileItem item) {
|
|
// ArrayList<FileItem> items = new ArrayList<FileItem>();
|
|
//// FileItem which = item.isDirectory() ? item : (FileItem) item.getParent();
|
|
//// FileItem which = item;
|
|
// FileItem which = (FileItem) item.getParent();
|
|
// while (which != null) {
|
|
// items.add(0, which);
|
|
// which = (FileItem) which.getParent();
|
|
// }
|
|
// return new TreePath(items.toArray());
|
|
//// FileItem[] array = items.toArray();
|
|
//// return new TreePath(array);
|
|
// }
|
|
|
|
|
|
// public static void loadExpansionState(JTree tree, Enumeration enumeration) {
|
|
// if (enumeration != null) {
|
|
// while (enumeration.hasMoreElements()) {
|
|
// TreePath treePath = (TreePath) enumeration.nextElement();
|
|
// tree.expandPath(treePath);
|
|
// }
|
|
// }
|
|
// }
|
|
|
|
|
|
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
|
|
|
|
|
|
protected JFrame sketchbookFrame;
|
|
|
|
public DefaultMutableTreeNode buildSketchbookTree() {
|
|
DefaultMutableTreeNode sbNode =
|
|
new DefaultMutableTreeNode(Language.text("sketchbook.tree"));
|
|
try {
|
|
base.addSketches(sbNode, Base.getSketchbookFolder());
|
|
} catch (IOException e) {
|
|
e.printStackTrace();
|
|
}
|
|
return sbNode;
|
|
}
|
|
|
|
|
|
/** Sketchbook has changed, update it on next viewing. */
|
|
public void rebuildSketchbookFrame() {
|
|
boolean visible =
|
|
(sketchbookFrame == null) ? false : sketchbookFrame.isVisible();
|
|
sketchbookFrame = null;
|
|
if (visible) {
|
|
showSketchbookFrame();
|
|
}
|
|
}
|
|
|
|
|
|
public void showSketchbookFrame() {
|
|
if (sketchbookFrame == null) {
|
|
sketchbookFrame = new JFrame(Language.text("sketchbook"));
|
|
Toolkit.setIcon(sketchbookFrame);
|
|
final ActionListener listener = new ActionListener() {
|
|
public void actionPerformed(ActionEvent e) {
|
|
sketchbookFrame.setVisible(false);
|
|
}
|
|
};
|
|
Toolkit.registerWindowCloseKeys(sketchbookFrame.getRootPane(), listener);
|
|
|
|
final JTree tree = new JTree(buildSketchbookTree());
|
|
tree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
|
|
tree.setShowsRootHandles(true);
|
|
tree.expandRow(0);
|
|
tree.setRootVisible(false);
|
|
|
|
tree.addMouseListener(new MouseAdapter() {
|
|
public void mouseClicked(MouseEvent e) {
|
|
if (e.getClickCount() == 2) {
|
|
DefaultMutableTreeNode node =
|
|
(DefaultMutableTreeNode) tree.getLastSelectedPathComponent();
|
|
|
|
int selRow = tree.getRowForLocation(e.getX(), e.getY());
|
|
//TreePath selPath = tree.getPathForLocation(e.getX(), e.getY());
|
|
//if (node != null && node.isLeaf() && node.getPath().equals(selPath)) {
|
|
if (node != null && node.isLeaf() && selRow != -1) {
|
|
SketchReference sketch = (SketchReference) node.getUserObject();
|
|
base.handleOpen(sketch.getPath());
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
tree.addKeyListener(new KeyAdapter() {
|
|
public void keyPressed(KeyEvent e) {
|
|
if (e.getKeyCode() == KeyEvent.VK_ESCAPE) { // doesn't fire keyTyped()
|
|
sketchbookFrame.setVisible(false);
|
|
}
|
|
}
|
|
|
|
public void keyTyped(KeyEvent e) {
|
|
if (e.getKeyChar() == KeyEvent.VK_ENTER) {
|
|
DefaultMutableTreeNode node = (DefaultMutableTreeNode) tree
|
|
.getLastSelectedPathComponent();
|
|
if (node != null && node.isLeaf()) {
|
|
SketchReference sketch = (SketchReference) node.getUserObject();
|
|
base.handleOpen(sketch.getPath());
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
tree.setBorder(new EmptyBorder(5, 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));
|
|
sketchbookFrame.getContentPane().add(treePane);
|
|
sketchbookFrame.pack();
|
|
}
|
|
|
|
EventQueue.invokeLater(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
// Space for the editor plus a li'l gap
|
|
int roughWidth = sketchbookFrame.getWidth() + 20;
|
|
Point p = null;
|
|
// If no window open, or the editor is at the edge of the screen
|
|
if (base.activeEditor == null ||
|
|
(p = base.activeEditor.getLocation()).x < roughWidth) {
|
|
// Center the window on the screen
|
|
sketchbookFrame.setLocationRelativeTo(null);
|
|
} else {
|
|
// Open the window relative to the editor
|
|
sketchbookFrame.setLocation(p.x - roughWidth, p.y);
|
|
}
|
|
sketchbookFrame.setVisible(true);
|
|
}
|
|
});
|
|
}
|
|
|
|
|
|
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
|
|
|
|
|
|
/**
|
|
* Get an ImageIcon object from the Mode folder.
|
|
* Or when prefixed with /lib, load it from the main /lib folder.
|
|
* @since 3.0a6
|
|
*/
|
|
public ImageIcon loadIcon(String filename) {
|
|
if (filename.startsWith("/lib/")) {
|
|
return Toolkit.getLibIcon(filename.substring(5));
|
|
}
|
|
File file = new File(folder, filename);
|
|
if (!file.exists()) {
|
|
// EditorConsole.systemErr.println("file does not exist: " + file.getAbsolutePath());
|
|
return null;
|
|
}
|
|
// EditorConsole.systemErr.println("found: " + file.getAbsolutePath());
|
|
return new ImageIcon(file.getAbsolutePath());
|
|
}
|
|
|
|
|
|
/**
|
|
* Get an image object from the mode folder.
|
|
* Or when prefixed with /lib, load it from the main /lib folder.
|
|
*/
|
|
public Image loadImage(String filename) {
|
|
ImageIcon icon = loadIcon(filename);
|
|
if (icon != null) {
|
|
return icon.getImage();
|
|
}
|
|
return null;
|
|
}
|
|
|
|
|
|
// public EditorButton loadButton(String name) {
|
|
// return new EditorButton(this, name);
|
|
// }
|
|
|
|
|
|
//public Settings getTheme() {
|
|
// return theme;
|
|
//}
|
|
|
|
|
|
/**
|
|
* Returns the HTML filename (including path prefix if necessary)
|
|
* for this keyword, or null if it doesn't exist.
|
|
*/
|
|
public String lookupReference(String keyword) {
|
|
return keywordToReference.get(keyword);
|
|
}
|
|
|
|
|
|
//public TokenMarker getTokenMarker() throws IOException {
|
|
// File keywordsFile = new File(folder, "keywords.txt");
|
|
// return new PdeKeywords(keywordsFile);
|
|
//}
|
|
public TokenMarker getTokenMarker() {
|
|
return tokenMarker;
|
|
}
|
|
|
|
protected TokenMarker createTokenMarker() {
|
|
return new PdeKeywords();
|
|
}
|
|
|
|
|
|
// abstract public Formatter createFormatter();
|
|
|
|
|
|
// public Formatter getFormatter() {
|
|
// return formatter;
|
|
// }
|
|
|
|
|
|
// public Tool getFormatter() {
|
|
// return formatter;
|
|
// }
|
|
|
|
|
|
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
|
|
|
|
// Get attributes/values from the theme.txt file. To discourage burying this
|
|
// kind of information in code where it doesn't belong (and is difficult to
|
|
// track down), these don't have a "default" option as a second parameter.
|
|
|
|
|
|
/** @since 3.0a6 */
|
|
public String getString(String attribute) {
|
|
return theme.get(attribute);
|
|
}
|
|
|
|
|
|
public boolean getBoolean(String attribute) {
|
|
return theme.getBoolean(attribute);
|
|
}
|
|
|
|
|
|
public int getInteger(String attribute) {
|
|
return theme.getInteger(attribute);
|
|
}
|
|
|
|
|
|
public Color getColor(String attribute) {
|
|
return theme.getColor(attribute);
|
|
}
|
|
|
|
|
|
public Font getFont(String attribute) {
|
|
return theme.getFont(attribute);
|
|
}
|
|
|
|
|
|
public SyntaxStyle getStyle(String attribute) {
|
|
String str = Preferences.get("editor.token." + attribute + ".style");
|
|
if (str == null) {
|
|
throw new IllegalArgumentException("No style found for " + attribute);
|
|
}
|
|
|
|
StringTokenizer st = new StringTokenizer(str, ",");
|
|
|
|
String s = st.nextToken();
|
|
if (s.indexOf("#") == 0) s = s.substring(1);
|
|
Color color = new Color(Integer.parseInt(s, 16));
|
|
|
|
s = st.nextToken();
|
|
boolean bold = (s.indexOf("bold") != -1);
|
|
// boolean italic = (s.indexOf("italic") != -1);
|
|
|
|
// return new SyntaxStyle(color, italic, bold);
|
|
return new SyntaxStyle(color, bold);
|
|
}
|
|
|
|
|
|
public Image makeGradient(String attribute, int wide, int high) {
|
|
int top = getColor(attribute + ".gradient.top").getRGB();
|
|
int bot = getColor(attribute + ".gradient.bottom").getRGB();
|
|
|
|
// float r1 = (top >> 16) & 0xff;
|
|
// float g1 = (top >> 8) & 0xff;
|
|
// float b1 = top & 0xff;
|
|
// float r2 = (bot >> 16) & 0xff;
|
|
// float g2 = (bot >> 8) & 0xff;
|
|
// float b2 = bot & 0xff;
|
|
|
|
BufferedImage outgoing =
|
|
new BufferedImage(wide, high, BufferedImage.TYPE_INT_RGB);
|
|
int[] row = new int[wide];
|
|
WritableRaster wr = outgoing.getRaster();
|
|
for (int i = 0; i < high; i++) {
|
|
// Arrays.fill(row, (255 - (i + GRADIENT_TOP)) << 24);
|
|
// int r = (int) PApplet.map(i, 0, high-1, r1, r2);
|
|
int rgb = PApplet.lerpColor(top, bot, i / (float)(high-1), PConstants.RGB);
|
|
Arrays.fill(row, rgb);
|
|
// System.out.println(PApplet.hex(row[0]));
|
|
wr.setDataElements(0, i, wide, 1, row);
|
|
}
|
|
// Graphics g = outgoing.getGraphics();
|
|
// for (int i = 0; i < steps; i++) {
|
|
// g.setColor(new Color(1, 1, 1, 255 - (i + GRADIENT_TOP)));
|
|
// //g.fillRect(0, i, EditorButton.DIM, 10);
|
|
// g.drawLine(0, i, EditorButton.DIM, i);
|
|
// }
|
|
return outgoing;
|
|
}
|
|
|
|
|
|
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
|
|
|
|
|
|
// Breaking out extension types in order to clean up the code, and make it
|
|
// easier for other environments (like Arduino) to incorporate changes.
|
|
|
|
|
|
/**
|
|
* True if the specified extension should be hidden when shown on a tab.
|
|
* For Processing, this is true for .pde files. (Broken out for subclasses.)
|
|
* You can override this in your Mode subclass to handle it differently.
|
|
*/
|
|
public boolean hideExtension(String what) {
|
|
return what.equals(getDefaultExtension());
|
|
}
|
|
|
|
|
|
/**
|
|
* True if the specified code has the default file extension.
|
|
*/
|
|
public boolean isDefaultExtension(SketchCode code) {
|
|
return code.getExtension().equals(getDefaultExtension());
|
|
}
|
|
|
|
|
|
/**
|
|
* True if the specified extension is the default file extension.
|
|
*/
|
|
public boolean isDefaultExtension(String what) {
|
|
return what.equals(getDefaultExtension());
|
|
}
|
|
|
|
|
|
/**
|
|
* @param f File to be checked against this mode's accepted extensions.
|
|
* @return Whether or not the given file name features an extension supported by this mode.
|
|
*/
|
|
public boolean canEdit(final File f) {
|
|
final int dot = f.getName().lastIndexOf('.');
|
|
if (dot < 0) {
|
|
return false;
|
|
}
|
|
return validExtension(f.getName().substring(dot + 1));
|
|
}
|
|
|
|
/**
|
|
* Check this extension (no dots, please) against the list of valid
|
|
* extensions.
|
|
*/
|
|
public boolean validExtension(String what) {
|
|
String[] ext = getExtensions();
|
|
for (int i = 0; i < ext.length; i++) {
|
|
if (ext[i].equals(what)) return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
/**
|
|
* Returns the default extension for this editor setup.
|
|
*/
|
|
abstract public String getDefaultExtension();
|
|
|
|
|
|
/**
|
|
* Returns the appropriate file extension to use for auxilliary source files in a sketch.
|
|
* For example, in a Java-mode sketch, auxilliary files should be name "Foo.java"; in
|
|
* Python mode, they should be named "foo.py".
|
|
*
|
|
* <p>Modes that do not override this function will get the default behavior of returning the
|
|
* default extension.
|
|
*/
|
|
public String getModuleExtension() {
|
|
return getDefaultExtension();
|
|
}
|
|
|
|
|
|
/**
|
|
* Returns a String[] array of proper extensions.
|
|
*/
|
|
abstract public String[] getExtensions();
|
|
|
|
|
|
/**
|
|
* Get array of file/directory names that needn't be copied during "Save As".
|
|
*/
|
|
abstract public String[] getIgnorable();
|
|
|
|
|
|
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
|
|
|
|
/**
|
|
* Checks coreLibraries and contribLibraries for a library with the specified name
|
|
* @param libName the name of the library to find
|
|
* @return the Library or null if not found
|
|
*/
|
|
public Library findLibraryByName(String libName) {
|
|
|
|
for (Library lib : this.coreLibraries) {
|
|
if (libName.equals(lib.getName()))
|
|
return lib;
|
|
}
|
|
|
|
for (Library lib : this.contribLibraries) {
|
|
if (libName.equals(lib.getName()))
|
|
return lib;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Create a fresh applet/application folder if the 'delete target folder'
|
|
* pref has been set in the preferences.
|
|
*/
|
|
public void prepareExportFolder(File targetFolder) {
|
|
if (targetFolder != null) {
|
|
// Nuke the old applet/application folder because it can cause trouble
|
|
if (Preferences.getBoolean("export.delete_target_folder")) {
|
|
// System.out.println("temporarily skipping deletion of " + targetFolder);
|
|
Util.removeDir(targetFolder);
|
|
// targetFolder.renameTo(dest);
|
|
}
|
|
// Create a fresh output folder (needed before preproc is run next)
|
|
targetFolder.mkdirs();
|
|
}
|
|
}
|
|
|
|
// public void handleNew() {
|
|
// base.handleNew();
|
|
// }
|
|
//
|
|
//
|
|
// public void handleNewReplace() {
|
|
// base.handleNewReplace();
|
|
// }
|
|
|
|
@Override
|
|
public String toString() {
|
|
return getTitle();
|
|
}
|
|
}
|