Files
processing4/pdex/src/processing/mode/experimental/ExperimentalMode.java
T
2014-11-15 11:14:01 -07:00

460 lines
17 KiB
Java
Executable File

/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
Part of the Processing project - http://processing.org
Copyright (c) 2012 The Processing Foundation
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2
as published by the Free Software Foundation.
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.mode.experimental;
import galsasson.mode.tweak.SketchParser;
import java.awt.Color;
import java.io.File;
import java.io.IOException;
import java.util.logging.FileHandler;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.ImageIcon;
import processing.app.Base;
import processing.app.Editor;
import processing.app.EditorState;
import processing.app.Mode;
import processing.app.Preferences;
import processing.app.RunnerListener;
import processing.app.Sketch;
import processing.app.SketchCode;
import processing.app.SketchException;
import processing.mode.java.JavaBuild;
import processing.mode.java.JavaMode;
import processing.mode.java.runner.Runner;
/**
* Experimental Mode for Processing, combines Debug Mode and XQMode and
* starts us working toward our next generation editor/debugger setup.
*/
public class ExperimentalMode extends JavaMode {
public static final boolean VERBOSE_LOGGING = true;
//public static final boolean VERBOSE_LOGGING = false;
public static final int LOG_SIZE = 512 * 1024; // max log file size (in bytes)
public static boolean DEBUG = !true;
public ExperimentalMode(Base base, File folder) {
super(base, folder);
// use libraries folder from javamode. will make sketches using core libraries work, as well as import libraries and examples menus
for (Mode m : base.getModeList()) {
if (m.getClass() == JavaMode.class) {
JavaMode jMode = (JavaMode) m;
librariesFolder = jMode.getLibrariesFolder();
rebuildLibraryList();
break;
}
}
// Fetch examples and reference from java mode
// thx to Manindra (https://github.com/martinleopold/DebugMode/issues/4)
examplesFolder = Base.getContentFile("modes/java/examples");
// https://github.com/martinleopold/DebugMode/issues/6
referenceFolder = Base.getContentFile("modes/java/reference");
// set logging level
Logger globalLogger = Logger.getLogger("");
//Logger logger = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME); // doesn't work on os x
if (VERBOSE_LOGGING) {
globalLogger.setLevel(Level.INFO);
} else {
globalLogger.setLevel(Level.WARNING);
}
// enable logging to file
try {
// settings is writable for built-in modes, mode folder is not writable
File logFolder = Base.getSettingsFile("debug");
if (!logFolder.exists()) {
logFolder.mkdir();
}
File logFile = new File(logFolder, "DebugMode.%g.log");
Handler handler = new FileHandler(logFile.getAbsolutePath(), LOG_SIZE, 10, false);
globalLogger.addHandler(handler);
} catch (IOException ex) {
Logger.getLogger(ExperimentalMode.class.getName()).log(Level.SEVERE, null, ex);
} catch (SecurityException ex) {
Logger.getLogger(ExperimentalMode.class.getName()).log(Level.SEVERE, null, ex);
}
// disable initial chattiness for now
// // output version from manifest file
// Package p = ExperimentalMode.class.getPackage();
// String titleAndVersion = p.getImplementationTitle() + " (v" + p.getImplementationVersion() + ")";
// //log(titleAndVersion);
// Logger.getLogger(ExperimentalMode.class.getName()).log(Level.INFO, titleAndVersion);
loadPreferences();
loadIcons();
}
@Override
public String getTitle() {
//return "PDE X";
return "Java";
}
public File[] getKeywordFiles() {
return new File[] {
Base.getContentFile("modes/java/keywords.txt")
};
}
public File getContentFile(String path) {
File file = new File(folder, path);
if (!file.exists()) {
// check to see if it's part of the parent Java Mode
file = new File(Base.getContentFile("modes/java"), path);
}
return file;
}
volatile public static boolean errorCheckEnabled = true,
warningsEnabled = true, codeCompletionsEnabled = true,
debugOutputEnabled = false, errorLogsEnabled = false,
autoSaveEnabled = true, autoSavePromptEnabled = true,
defaultAutoSaveEnabled = true, // ,untitledAutoSaveEnabled;
ccTriggerEnabled = true, importSuggestEnabled = true;
public static int autoSaveInterval = 3; //in minutes
/**
* After how many typed characters, code completion is triggered
*/
volatile public static int codeCompletionTriggerLength = 2;
static public final String prefErrorCheck = "pdex.errorCheckEnabled";
static public final String prefWarnings = "pdex.warningsEnabled";
static public final String prefCodeCompletionEnabled = "pdex.completion";
static public final String prefCCTriggerEnabled = "pdex.completion.trigger";
static public final String prefDebugOP = "pdex.dbgOutput";
static public final String prefErrorLogs = "pdex.writeErrorLogs";
static public final String prefAutoSaveInterval = "pdex.autoSaveInterval";
static public final String prefAutoSave = "pdex.autoSave.autoSaveEnabled";
static public final String prefAutoSavePrompt = "pdex.autoSave.promptDisplay";
static public final String prefDefaultAutoSave = "pdex.autoSave.autoSaveByDefault";
static public final String prefImportSuggestEnabled = "pdex.importSuggestEnabled";
static volatile public boolean enableTweak = false;
public void loadPreferences() {
log("Load PDEX prefs");
ensurePrefsExist();
errorCheckEnabled = Preferences.getBoolean(prefErrorCheck);
warningsEnabled = Preferences.getBoolean(prefWarnings);
codeCompletionsEnabled = Preferences.getBoolean(prefCodeCompletionEnabled);
DEBUG = Preferences.getBoolean(prefDebugOP);
errorLogsEnabled = Preferences.getBoolean(prefErrorLogs);
autoSaveInterval = Preferences.getInteger(prefAutoSaveInterval);
// untitledAutoSaveEnabled = Preferences.getBoolean(prefUntitledAutoSave);
autoSaveEnabled = Preferences.getBoolean(prefAutoSave);
autoSavePromptEnabled = Preferences.getBoolean(prefAutoSavePrompt);
defaultAutoSaveEnabled = Preferences.getBoolean(prefDefaultAutoSave);
ccTriggerEnabled = Preferences.getBoolean(prefCCTriggerEnabled);
importSuggestEnabled = Preferences.getBoolean(prefImportSuggestEnabled);
}
public void savePreferences() {
log("Saving PDEX prefs");
Preferences.setBoolean(prefErrorCheck, errorCheckEnabled);
Preferences.setBoolean(prefWarnings, warningsEnabled);
Preferences.setBoolean(prefCodeCompletionEnabled, codeCompletionsEnabled);
Preferences.setBoolean(prefDebugOP, DEBUG);
Preferences.setBoolean(prefErrorLogs, errorLogsEnabled);
Preferences.setInteger(prefAutoSaveInterval, autoSaveInterval);
// Preferences.setBoolean(prefUntitledAutoSave,untitledAutoSaveEnabled);
Preferences.setBoolean(prefAutoSave, autoSaveEnabled);
Preferences.setBoolean(prefAutoSavePrompt, autoSavePromptEnabled);
Preferences.setBoolean(prefDefaultAutoSave, defaultAutoSaveEnabled);
Preferences.setBoolean(prefCCTriggerEnabled, ccTriggerEnabled);
Preferences.setBoolean(prefImportSuggestEnabled, importSuggestEnabled);
}
public void ensurePrefsExist() {
//TODO: Need to do a better job of managing prefs. Think lists.
if (Preferences.get(prefErrorCheck) == null)
Preferences.setBoolean(prefErrorCheck, errorCheckEnabled);
if (Preferences.get(prefWarnings) == null)
Preferences.setBoolean(prefWarnings, warningsEnabled);
if (Preferences.get(prefCodeCompletionEnabled) == null)
Preferences.setBoolean(prefCodeCompletionEnabled, codeCompletionsEnabled);
if (Preferences.get(prefDebugOP) == null)
Preferences.setBoolean(prefDebugOP, DEBUG);
if (Preferences.get(prefErrorLogs) == null)
Preferences.setBoolean(prefErrorLogs, errorLogsEnabled);
if (Preferences.get(prefAutoSaveInterval) == null)
Preferences.setInteger(prefAutoSaveInterval, autoSaveInterval);
// if(Preferences.get(prefUntitledAutoSave) == null)
// Preferences.setBoolean(prefUntitledAutoSave,untitledAutoSaveEnabled);
if (Preferences.get(prefAutoSave) == null)
Preferences.setBoolean(prefAutoSave, autoSaveEnabled);
if (Preferences.get(prefAutoSavePrompt) == null)
Preferences.setBoolean(prefAutoSavePrompt, autoSavePromptEnabled);
if (Preferences.get(prefDefaultAutoSave) == null)
Preferences.setBoolean(prefDefaultAutoSave, defaultAutoSaveEnabled);
if (Preferences.get(prefCCTriggerEnabled) == null)
Preferences.setBoolean(prefCCTriggerEnabled, ccTriggerEnabled);
if (Preferences.get(prefImportSuggestEnabled) == null)
Preferences.setBoolean(prefImportSuggestEnabled, importSuggestEnabled);
}
/**
* Create a new editor associated with this mode.
*/
@Override
public Editor createEditor(Base base, String path, EditorState state) {
return new DebugEditor(base, path, state, this);
}
/**
* Load a String value from theme.txt
*
* @param attribute the attribute key to load
* @param defaultValue the default value
* @return the attributes value, or the default value if the attribute
* couldn't be loaded
*/
public String loadThemeString(String attribute, String defaultValue) {
String newString = theme.get(attribute);
if (newString != null) {
return newString;
}
Logger.getLogger(getClass().getName()).log(Level.WARNING, "Error loading String: {0}", attribute);
return defaultValue;
}
/**
* Load a Color value from theme.txt
*
* @param attribute the attribute key to load
* @param defaultValue the default value
* @return the attributes value, or the default value if the attribute
* couldn't be loaded
*/
public Color getThemeColor(String attribute, Color defaultValue) {
Color newColor = theme.getColor(attribute);
if (newColor != null) {
return newColor;
}
log("error loading color: " + attribute);
Logger.getLogger(ExperimentalMode.class.getName()).log(Level.WARNING, "Error loading Color: {0}", attribute);
return defaultValue;
}
protected ImageIcon classIcon, fieldIcon, methodIcon, localVarIcon;
protected void loadIcons(){
String iconPath = getContentFile("data")
.getAbsolutePath()
+ File.separator + "icons";
classIcon = new ImageIcon(iconPath + File.separator + "class_obj.png");
methodIcon = new ImageIcon(iconPath + File.separator
+ "methpub_obj.png");
fieldIcon = new ImageIcon(iconPath + File.separator
+ "field_protected_obj.png");
localVarIcon = new ImageIcon(iconPath + File.separator
+ "field_default_obj.png");
// log("Icons loaded");
}
public ClassLoader getJavaModeClassLoader() {
for (Mode m : base.getModeList()) {
if (m.getClass() == JavaMode.class) {
JavaMode jMode = (JavaMode) m;
return jMode.getClassLoader();
}
}
// badness
return null;
}
/**
* System.out.println()
*/
public static final void log(Object message){
if(ExperimentalMode.DEBUG)
System.out.println(message);
}
/**
* System.err.println()
*/
public static final void logE(Object message){
if(ExperimentalMode.DEBUG)
System.err.println(message);
}
/**
* System.out.print
*/
public static final void log2(Object message){
if(ExperimentalMode.DEBUG)
System.out.print(message);
}
public String[] getIgnorable() {
return new String[] {
"applet",
"application.macosx",
"application.windows",
"application.linux",
"_autosave"
};
}
// TweakMode code
@Override
public Runner handleRun(Sketch sketch, RunnerListener listener) throws SketchException
{
final DebugEditor editor = (DebugEditor)listener;
editor.errorCheckerService.quickErrorCheck();
if (enableTweak) {
enableTweak = false;
return handleTweakPresentOrRun(sketch, listener, false);
}
else {
/* Do the usual (JavaMode style) */
JavaBuild build = new JavaBuild(sketch);
String appletClassName = build.build(false);
if (appletClassName != null) {
final Runner runtime = new Runner(build, listener);
new Thread(new Runnable() {
public void run() {
runtime.launch(false); // this blocks until finished
}
}).start();
return runtime;
}
return null;
}
}
@Override
public Runner handlePresent(Sketch sketch, RunnerListener listener) throws SketchException
{
final DebugEditor editor = (DebugEditor)listener;
editor.errorCheckerService.quickErrorCheck();
if (enableTweak) {
enableTweak = false;
return handleTweakPresentOrRun(sketch, listener, true);
}
else {
/* Do the usual (JavaMode style) */
JavaBuild build = new JavaBuild(sketch);
String appletClassName = build.build(false);
if (appletClassName != null) {
final Runner runtime = new Runner(build, listener);
new Thread(new Runnable() {
public void run() {
runtime.launch(true);
}
}).start();
return runtime;
}
return null;
}
}
public Runner handleTweakPresentOrRun(Sketch sketch, RunnerListener listener, boolean present) throws SketchException
{
final DebugEditor editor = (DebugEditor)listener;
final boolean toPresent = present;
boolean launchInteractive = false;
if (isSketchModified(sketch)) {
editor.deactivateRun();
Base.showMessage("Save", "Please save the sketch before running in Tweak Mode.");
return null;
}
/* first try to build the unmodified code */
JavaBuild build = new JavaBuild(sketch);
String appletClassName = build.build(false);
if (appletClassName == null) {
// unmodified build failed, so fail
return null;
}
/* if compilation passed, modify the code and build again */
// save the original sketch code of the user
editor.initBaseCode();
// check for "// tweak" comment in the sketch
boolean requiresTweak = SketchParser.containsTweakComment(editor.baseCode);
// parse the saved sketch to get all (or only with "//tweak" comment) numbers
final SketchParser parser = new SketchParser(editor.baseCode, requiresTweak);
// add our code to the sketch
launchInteractive = editor.automateSketch(sketch, parser.allHandles);
build = new JavaBuild(sketch);
appletClassName = build.build(false);
if (appletClassName != null) {
final Runner runtime = new Runner(build, listener);
new Thread(new Runnable() {
public void run() {
runtime.launch(toPresent); // this blocks until finished
// executed when the sketch quits
editor.initEditorCode(parser.allHandles, false);
editor.stopInteractiveMode(parser.allHandles);
}
}).start();
if (launchInteractive) {
// replace editor code with baseCode
editor.initEditorCode(parser.allHandles, false);
editor.updateInterface(parser.allHandles, parser.colorBoxes);
editor.startInteractiveMode();
}
return runtime;
}
return null;
}
private boolean isSketchModified(Sketch sketch)
{
for (SketchCode sc : sketch.getCode()) {
if (sc.isModified()) {
return true;
}
}
return false;
}
}