Java tutorial
// -*- mode: java; c-basic-offset: 2; -*- // Copyright 2009-2011 Google, All Rights reserved // Copyright 2011-2012 MIT, All rights reserved // Released under the Apache License, Version 2.0 // http://www.apache.org/licenses/LICENSE-2.0 package com.google.appinventor.client.editor.youngandroid; import com.google.appinventor.client.ComponentsTranslation; import com.google.appinventor.client.DesignToolbar; import com.google.appinventor.client.ErrorReporter; import com.google.appinventor.client.Ode; import com.google.appinventor.client.TopToolbar; import com.google.appinventor.client.TranslationComponentParams; import com.google.appinventor.client.TranslationDesignerPallete; import com.google.appinventor.client.editor.simple.SimpleComponentDatabase; import com.google.appinventor.client.explorer.project.ComponentDatabaseChangeListener; import com.google.appinventor.client.output.OdeLog; import com.google.appinventor.components.common.YaVersion; import com.google.common.collect.Maps; import com.google.gwt.core.client.JavaScriptException; import com.google.gwt.core.client.JavaScriptObject; import com.google.gwt.user.client.ui.Button; import com.google.gwt.user.client.ui.ClickListener; import com.google.gwt.user.client.ui.DialogBox; import com.google.gwt.user.client.ui.HTML; import com.google.gwt.user.client.ui.HTMLPanel; import com.google.gwt.user.client.ui.HorizontalPanel; import com.google.gwt.user.client.ui.VerticalPanel; import com.google.gwt.user.client.ui.Widget; import com.google.gwt.user.client.Timer; import com.google.gwt.user.client.Window; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import static com.google.appinventor.client.Ode.MESSAGES; /** * Blocks editor panel. * The contents of the blocks editor panel is in an iframe identified by * the formName passed to the constructor. That identifier is also the hashtag * on the URL that is the source of the iframe. This class provides methods for * calling the Javascript Blockly code from the rest of the Designer. * * @author sharon@google.com (Sharon Perl) */ public class BlocklyPanel extends HTMLPanel implements ComponentDatabaseChangeListener { public static enum OpType { ADD, REMOVE, RENAME } // The currently displayed form (project/screen) private static String currentForm; private static String languageSetting; private static class ComponentOp { public OpType op; public String instanceName; // for ADD, REMOVE, RENAME public String uid; // for ADD, REMOVE, RENAME public String typeDescription; // for ADD public String typeName; // for REMOVE, RENAME public String oldName; // for RENAME } private static class LoadStatus { public boolean complete = false; // true if loading blocks completed public boolean error = false; // true if got an error loading blocks } private final SimpleComponentDatabase COMPONENT_DATABASE; private static final String EDITOR_HTML = "<style>\n" + ".svg {\n" + " height: 100%;\n" + " width: 100%;\n" + " border: solid black 1px;\n" + "}\n" + "</style>\n" + "<iframe src=\"blocklyframe.html#FORM_NAME\" class=\"svg\">"; // Keep track of component additions/removals/renames that happen before // blocks editor is inited for the first time, or before reinitialization // after the blocks editor's project has been detached from the document. // Replay them in order after initialized. Keys are form names. If there is // an entry for a given form name in the map, its blocks have not yet been // (re)inited. // Note: Javascript is single-threaded. Since this code is compiled by GWT // into Javascript, we don't need to worry about concurrent access to // this map. private static Map<String, List<ComponentOp>> componentOps = Maps.newHashMap(); // When a user switches projects, the ProjectEditor widget gets detached // from the main document in the browser. If the user switches back to a // previously open project (in the same session), when the ProjectEditor // widget gets reattached, all of its FileEditors in its deckPanel get // reloaded, causing the Blockly objects for the blocks editors // to be created anew. Since the FileEditor Java objects themselves are // not recreated, we need to reconstruct the set of components in the Blockly // object when the object gets recreated. For each form, we keep track of the // components currently in that form, stored as "add" operations that can be // replayed to restore those components when the underlying Blockly state // is re-inited. This component state is updated as components are added, // removed, and renamed. The outer map is keyed by form name, and the // inner map is keyed by component uid. private static final Map<String, Map<String, ComponentOp>> currentComponents = Maps.newHashMap(); // Pending blocks file content, indexed by form name. Waiting to be loaded when the corresponding // blocks area is initialized. private static final Map<String, String> pendingBlocksContentMap = Maps.newHashMap(); // [lyn, 2014/10/27] added formJson for upgrading // Pending form JSON content, indexed by form name. Waiting to be loaded when the corresponding // blocks area is initialized. private static final Map<String, String> pendingFormJsonMap = Maps.newHashMap(); // Status of blocks loading, indexed by form name. private static final Map<String, LoadStatus> loadStatusMap = Maps.newHashMap(); // Blockly backpack private static String backpack = "[]"; // My form name private String formName; // My blocks editor private YaBlocksEditor myBlocksEditor; // [lyn, 2014/10/28] Added to access current form json public static boolean isWarningVisible = false; public BlocklyPanel(YaBlocksEditor blocksEditor, String formName) { super(EDITOR_HTML.replace("FORM_NAME", formName)); this.formName = formName; this.myBlocksEditor = blocksEditor; COMPONENT_DATABASE = SimpleComponentDatabase.getInstance(blocksEditor.getProjectId()); componentOps.put(formName, new ArrayList<ComponentOp>()); // note: using Maps.newHashMap() gives a type error in Eclipse in the following line currentComponents.put(formName, new HashMap<String, ComponentOp>()); initJS(); OdeLog.log("Created BlocklyPanel for " + formName); } /* * Do whatever is needed for App Inventor UI initialization. In this case * we just need to export the init components method so that we can * get called back from the Blockly Javascript when it finishes loading. */ public static void initUi() { exportMethodsToJavascript(); // Tell the blockly world about companion versions. setPreferredCompanion(YaVersion.PREFERRED_COMPANION, YaVersion.COMPANION_UPDATE_URL); for (int i = 0; i < YaVersion.ACCEPTABLE_COMPANIONS.length; i++) { addAcceptableCompanion(YaVersion.ACCEPTABLE_COMPANIONS[i]); } addAcceptableCompanionPackage(YaVersion.ACCEPTABLE_COMPANION_PACKAGE); } /* * Initialize the blocks area so that it can be updated as components are * added, removed, or changed. Replay any previous component operations that * we weren't able to run before the blocks editor was initialized. This * method is static so that it can be called by the native Javascript code * after it finishes loading. We export this method to Javascript in * exportInitComponentsMethod(). */ private static void initBlocksArea(String formName) { OdeLog.log("BlocklyPanel: Got initBlocksArea call for " + formName); // if there are any components added, add them first before we load // block content that might reference them Map<String, ComponentOp> savedComponents = currentComponents.get(formName); if (savedComponents != null) { // shouldn't be! OdeLog.log("Restoring " + savedComponents.size() + " previous blockly components for form " + formName); for (ComponentOp op : savedComponents.values()) { doAddComponent(formName, op.typeDescription, op.instanceName, op.uid); } } if (componentOps.containsKey(formName)) { OdeLog.log("Replaying " + componentOps.get(formName).size() + " ops waiting in queue"); for (ComponentOp op : componentOps.get(formName)) { switch (op.op) { case ADD: doAddComponent(formName, op.typeDescription, op.instanceName, op.uid); addSavedComponent(formName, op.typeDescription, op.instanceName, op.uid); break; case REMOVE: doRemoveComponent(formName, op.typeName, op.instanceName, op.uid); removeSavedComponent(formName, op.typeName, op.instanceName, op.uid); break; case RENAME: doRenameComponent(formName, op.typeName, op.oldName, op.instanceName, op.uid); renameSavedComponent(formName, op.typeName, op.oldName, op.instanceName, op.uid); break; } } componentOps.remove(formName); } // If we've gotten any block content to load, load it now // Note: Map.remove() returns the value (null if not present), as well as removing the mapping String pendingBlocksContent = pendingBlocksContentMap.remove(formName); // [lyn, 2014/10/27] added formJson for upgrading String pendingFormJson = pendingFormJsonMap.remove(formName); if (pendingBlocksContent != null) { OdeLog.log("Loading blocks area content for " + formName); loadBlocksContentNow(formName, pendingFormJson, pendingBlocksContent); } } private static void blocklyWorkspaceChanged(String formName) { LoadStatus loadStat = loadStatusMap.get(formName); // ignore workspaceChanged events until after the load finishes. if (loadStat == null || !loadStat.complete) { return; } if (loadStat.error) { YaBlocksEditor.setBlocksDamaged(formName); ErrorReporter.reportError(MESSAGES.blocksNotSaved(formName)); } else { YaBlocksEditor.onBlocksAreaChanged(formName); } } // Returns true if the blocks for formName have been initialized (i.e., // no componentOps entry exists for formName). public static boolean blocksInited(String formName) { return !componentOps.containsKey(formName); } public static String getBackpack() { return backpack; } public static void setBackpack(String bp_contents) { backpack = bp_contents; } /** * Add a component to the blocks workspace * * @param typeDescription JSON string describing the component type, * formatted as described in * {@link com.google.appinventor.components.scripts.ComponentDescriptorGenerator} * @param instanceName the name of the component instance * @param uid the unique id of the component instance */ public void addComponent(String typeDescription, String instanceName, String uid) { if (!blocksInited(formName)) { ComponentOp cop = new ComponentOp(); cop.op = OpType.ADD; cop.instanceName = instanceName; cop.typeDescription = typeDescription; cop.uid = uid; if (!componentOps.containsKey(formName)) { componentOps.put(formName, new ArrayList<ComponentOp>()); } componentOps.get(formName).add(cop); } else { doAddComponent(formName, typeDescription, instanceName, uid); addSavedComponent(formName, typeDescription, instanceName, uid); } } private static void addSavedComponent(String formName, String typeDescription, String instanceName, String uid) { Map<String, ComponentOp> myComponents = currentComponents.get(formName); if (!myComponents.containsKey(uid)) { // we expect there to be no saved component with this uid yet! ComponentOp savedComponent = new ComponentOp(); savedComponent.op = OpType.ADD; savedComponent.instanceName = instanceName; savedComponent.typeDescription = typeDescription; savedComponent.uid = uid; myComponents.put(uid, savedComponent); } else { OdeLog.wlog("BlocklyPanel: already have component with uid " + uid + ", instanceName is " + myComponents.get(uid).instanceName); } } /** * Remove the component instance instanceName, with the given typeName * and uid from the workspace. * * @param typeName component type name (e.g., "Canvas" or "Button") * @param instanceName instance name * @param uid unique id */ public void removeComponent(String typeName, String instanceName, String uid) { if (!blocksInited(formName)) { ComponentOp cop = new ComponentOp(); cop.op = OpType.REMOVE; cop.instanceName = instanceName; cop.typeName = typeName; cop.uid = uid; if (!componentOps.containsKey(formName)) { componentOps.put(formName, new ArrayList<ComponentOp>()); } componentOps.get(formName).add(cop); } else { doRemoveComponent(formName, typeName, instanceName, uid); removeSavedComponent(formName, typeName, instanceName, uid); } } private static void removeSavedComponent(String formName, String typeName, String instanceName, String uid) { Map<String, ComponentOp> myComponents = currentComponents.get(formName); if (myComponents.containsKey(uid) && myComponents.get(uid).instanceName.equals(instanceName)) { // we expect it to be there myComponents.remove(uid); } else { OdeLog.wlog("BlocklyPanel: can't find saved component with uid " + uid + " and name " + instanceName); } } /** * Rename the component whose old name is oldName (and whose * unique id is uid and type name is typeName) to newName. * * @param typeName component type name (e.g., "Canvas" or "Button") * @param oldName old instance name * @param newName new instance name * @param uid unique id */ public void renameComponent(String typeName, String oldName, String newName, String uid) { if (!blocksInited(formName)) { ComponentOp cop = new ComponentOp(); cop.op = OpType.RENAME; cop.instanceName = newName; cop.oldName = oldName; cop.typeName = typeName; cop.uid = uid; if (!componentOps.containsKey(formName)) { componentOps.put(formName, new ArrayList<ComponentOp>()); } componentOps.get(formName).add(cop); } else { doRenameComponent(formName, typeName, oldName, newName, uid); renameSavedComponent(formName, typeName, oldName, newName, uid); } } private static void renameSavedComponent(String formName, String typeName, String oldName, String newName, String uid) { Map<String, ComponentOp> myComponents = currentComponents.get(formName); if (myComponents.containsKey(uid)) { // we expect it to be there ComponentOp savedComponent = myComponents.get(uid); if (savedComponent.instanceName.equals(oldName)) { // it should! savedComponent.instanceName = newName; // rename saved component } else { OdeLog.wlog("BlocklyPanel: saved component with uid " + uid + " has name " + savedComponent.instanceName + ", expected " + oldName); } } else { OdeLog.wlog("BlocklyPanel: can't find saved component with uid " + uid + " and name " + oldName); } } /** * Show the drawer for component with the specified instance name * * @param name */ public void showComponentBlocks(String name) { if (blocksInited(formName)) { doShowComponentBlocks(formName, name); } } /** * Hide the component blocks drawer */ public void hideComponentBlocks() { if (blocksInited(formName)) { doHideComponentBlocks(formName); } } /** * Show the built-in blocks drawer with the specified name * * @param drawerName */ public void showBuiltinBlocks(String drawerName) { try { if (blocksInited(formName)) { doShowBuiltinBlocks(formName, drawerName); } } catch (JavaScriptException e) { ErrorReporter.reportInfo("Not yet implemented: " + drawerName); } } /** * Hide the built-in blocks drawer */ public void hideBuiltinBlocks() { if (blocksInited(formName)) { doHideBlocks(formName); } } /** * Show the generic blocks drawer with the specified name * * @param drawerName */ public void showGenericBlocks(String drawerName) { if (blocksInited(formName)) { doShowGenericBlocks(formName, drawerName); } } /** * Hide the generic blocks drawer */ public void hideGenericBlocks() { if (blocksInited(formName)) { doHideBlocks(formName); } } public void renderBlockly() { if (blocksInited(formName)) { doRenderBlockly(formName); } } public static void toggleWarning(String formName) { if (blocksInited(formName)) { doToggleWarning(formName); } } public static void switchWarningVisibility() { if (BlocklyPanel.isWarningVisible) { BlocklyPanel.isWarningVisible = false; } else { BlocklyPanel.isWarningVisible = true; } } public static void checkWarningState(String formName) { if (BlocklyPanel.isWarningVisible) { toggleWarning(formName); } doCheckWarnings(formName); } public static void callToggleWarning() { YaBlocksEditor.toggleWarning(); } /** * Remember any component instances for this form in case * the workspace gets reinitialized later (we get detached from * our parent object and then our blocks editor gets loaded * again later). Also, remember the current state of the blocks * area in case we get reloaded. */ public void saveComponentsAndBlocks() { // Actually, we already have the components saved, but take this as an // indication that we are going to reinit the blocks editor the next // time it is shown. OdeLog.log("BlocklyEditor: prepared for reinit for form " + formName); // Call doResetYail which will stop the timer that is polling the phone. It is important // that it be stopped to avoid a race condition where the last timer on this form fires // while the new form is loading. doResetYail(formName); // Get blocks content before putting anything in the componentOps map since an entry in // the componentOps map is taken as an indication that the blocks area has not initialized yet. pendingBlocksContentMap.put(formName, getBlocksContent()); // [lyn, 2014/10/28] added formJson for upgrading pendingFormJsonMap.put(formName, getFormJson()); componentOps.put(formName, new ArrayList<ComponentOp>()); } /** * @returns true if the blocks drawer is showing, false otherwise. */ public boolean drawerShowing() { if (blocksInited(formName)) { return doDrawerShowing(formName); } else { return false; } } /** * Load the blocks described by blocksContent into the blocks workspace. * * @param blocksContent XML description of a blocks workspace in format expected by Blockly */ // [lyn, 2014/10/27] added formJson for upgrading public void loadBlocksContent(String formJson, String blocksContent) { LoadStatus loadStat = new LoadStatus(); loadStatusMap.put(formName, loadStat); if (blocksInited(formName)) { OdeLog.log("Loading blocks content for " + formName); loadBlocksContentNow(formName, formJson, blocksContent); } else { // save it to load when the blocks area is initialized OdeLog.log("Caching blocks content for " + formName + " for loading when blocks area inited"); pendingBlocksContentMap.put(formName, blocksContent); // [lyn, 2014/10/27] added formJson for upgrading pendingFormJsonMap.put(formName, formJson); } } // [lyn, 2014/10/27] added formJson for upgrading public static void loadBlocksContentNow(String formName, String formJson, String blocksContent) { LoadStatus loadStat = loadStatusMap.get(formName); // should not be null! try { doLoadBlocksContent(formName, formJson, blocksContent); } catch (JavaScriptException e) { ErrorReporter.reportError(MESSAGES.blocksLoadFailure(formName)); OdeLog.elog("Error loading blocks for screen " + formName + ": " + e.getDescription()); loadStat.error = true; } loadStat.complete = true; } /** * Return the XML string describing the current state of the blocks workspace */ public String getBlocksContent() { if (blocksInited(formName)) { return doGetBlocksContent(formName); } else { // in case someone clicks Save before the blocks area is inited String blocksContent = pendingBlocksContentMap.get(formName); return (blocksContent != null) ? blocksContent : ""; } } /** * Return the JSON string describing the current state of the associated form */ // [lyn, 2014/10/28] Handle these cases public String getFormJson() { if (blocksInited(formName)) { return myBlocksEditor.encodeFormAsJsonString(true); } else { // in case someone clicks Save before the blocks area is inited String formJson = pendingFormJsonMap.get(formName); return (formJson != null) ? formJson : ""; } } /** * Get Yail code for current blocks workspace * * @return the yail code as a String * @throws YailGenerationException if there was a problem generating the Yail */ public String getYail(String formJson, String packageName) throws YailGenerationException { if (!blocksInited(formName)) { throw new YailGenerationException("Blocks area is not initialized yet", formName); } try { return doGetYail(formName, formJson, packageName); } catch (JavaScriptException e) { throw new YailGenerationException(e.getDescription(), formName); } } /** * Send component data (json and form name) to Blockly for building * yail for the REPL. * * @throws YailGenerationException if there was a problem generating the Yail */ public void sendComponentData(String formJson, String packageName) throws YailGenerationException { if (!currentForm.equals(formName)) { // Not working on the current form... OdeLog.log("Not working on " + currentForm + " (while sending for " + formName + ")"); return; } if (!blocksInited(formName)) { throw new YailGenerationException("Blocks area is not initialized yet", formName); } try { doSendJson(formName, formJson, packageName); } catch (JavaScriptException e) { throw new YailGenerationException(e.getDescription(), formName); } } public void showDifferentForm(String newFormName) { OdeLog.log("showDifferentForm changing from " + formName + " to " + newFormName); // Nuke Yail for form we are leaving so it will reload when we return if (!formName.equals(newFormName)) doResetYail(formName); formName = newFormName; blocklyWorkspaceChanged(formName); } public void startRepl(boolean alreadyRunning, boolean forEmulator, boolean forUsb) { // Start the Repl doStartRepl(formName, alreadyRunning, forEmulator, forUsb); } public void hardReset() { doHardReset(formName); } public static boolean checkIsAdmin() { return Ode.getInstance().getUser().getIsAdmin(); } // Set currentScreen // We use this to determine if we should send Yail to a // a connected device. public static void setCurrentForm(String formName) { currentForm = formName; if (blocksInited(formName)) blocklyWorkspaceChanged(formName); // Update the device now if the blocks are ready. } public static void indicateDisconnect() { TopToolbar.indicateDisconnect(); DesignToolbar.clearScreens(); } public static boolean pushScreen(String newScreen) { return DesignToolbar.pushScreen(newScreen); } public static void popScreen() { DesignToolbar.popScreen(); } // The code below (4 methods worth) is for creating a GWT dialog box // from the blockly code. See the comment in replmgr.js for more // information. /** * Create a Dialog Box. We call this from Javascript (blockly) to * display a dialog box. We do this here because we can get calls * from the blocklyframe when it is not visible. Because we are in * the parent window, we can display dialogs that will be visible * even when the blocklyframe is not visible. * * @param title Title for the Dialog Box * @param mess The message to display * @param buttonName The string to display in the "OK" button. * @param size 0 or 1. 0 makes a smaller box 1 makes a larger box. * @param callback an opague JavaScriptObject that contains the * callback function provided by the Javascript code. * @return The created dialog box. */ public static DialogBox createDialog(String title, String mess, final String buttonName, final String cancelButtonName, int size, final JavaScriptObject callback) { final DialogBox dialogBox = new DialogBox(); dialogBox.setStylePrimaryName("ode-DialogBox"); dialogBox.setText(title); if (size == 0) { dialogBox.setHeight("150px"); } else { dialogBox.setHeight("400px"); } dialogBox.setWidth("400px"); dialogBox.setGlassEnabled(true); dialogBox.setAnimationEnabled(true); dialogBox.center(); VerticalPanel DialogBoxContents = new VerticalPanel(); HTML message = new HTML(mess); message.setStyleName("DialogBox-message"); HorizontalPanel holder = new HorizontalPanel(); if (buttonName != null) { // If buttonName and cancelButtonName are null Button ok = new Button(buttonName); // We won't have any buttons and other ok.addClickListener(new ClickListener() { // code is needed to dismiss us public void onClick(Widget sender) { doCallBack(callback, buttonName); } }); holder.add(ok); } if (cancelButtonName != null) { Button cancel = new Button(cancelButtonName); cancel.addClickListener(new ClickListener() { public void onClick(Widget sender) { doCallBack(callback, cancelButtonName); } }); holder.add(cancel); } DialogBoxContents.add(message); DialogBoxContents.add(holder); dialogBox.setWidget(DialogBoxContents); dialogBox.show(); return dialogBox; } /** * Hide a dialog box. This function is here so it can be called from * the blockly code. We cannot call "hide" directly from the blockly * code because when this code is compiled, the "hide" method disappears! * * @param dialog The dialogbox to hide. */ public static void HideDialog(DialogBox dialog) { dialog.hide(); } public static void SetDialogContent(DialogBox dialog, String mess) { HTML html = (HTML) ((VerticalPanel) dialog.getWidget()).getWidget(0); html.setHTML(mess); } public static String getComponentInfo(String typeName) { return YaBlocksEditor.getComponentInfo(typeName); } public static String getComponentsJSONString() { return YaBlocksEditor.getComponentsJSONString(); } public static String getComponentInstanceTypeName(String formName, String instanceName) { return YaBlocksEditor.getComponentInstanceTypeName(formName, instanceName); } public static int getYaVersion() { return YaVersion.YOUNG_ANDROID_VERSION; } public static int getBlocksLanguageVersion() { return YaVersion.BLOCKS_LANGUAGE_VERSION; } public static String getQRCode(String inString) { if (currentForm == null) { // Cannot build a QR code without a current form return ""; // This only happens when you have no projects } return doQRCode(currentForm, inString); } /** * Update the language setting within BlocklyPanel.java and switch to the * desired language. * * @param newLanguage The desired new language setting */ public void switchLanguage(String newLanguage) { languageSetting = newLanguage; doSwitchLanguage(formName, languageSetting); } /** * Update the language setting within BlocklyPanel.java and switch to the * desired language. * * @param newLanguage The desired new language setting * @param formName */ public static void switchLanguage(String formName, String newLanguage) { languageSetting = newLanguage; doSwitchLanguage(formName, languageSetting); } /** * Trigger and Update of the Companion if the Companion is connected * and an update is available. Note: We do not compare the currently * running Companion's version against the version we are going to load * we just do it. If YaVersion.COMPANION_UPDATE_URL is "", then no * Update is available. */ public void updateCompanion() { updateCompanion(formName); } public static void updateCompanion(String formName) { doUpdateCompanion(formName); } public static String getLocalizedPropertyName(String key) { return ComponentsTranslation.getPropertyName(key); } public static String getLocalizedParameterName(String key) { return TranslationComponentParams.getName(key); } public static String getLocalizedMethodName(String key) { return ComponentsTranslation.getMethodName(key); } public static String getLocalizedEventName(String key) { return ComponentsTranslation.getEventName(key); } public static String getLocalizedComponentType(String key) { return TranslationDesignerPallete.getCorrespondingString(key); } @Override public void onComponentTypeAdded(List<String> componentTypes) { populateComponentTypes(formName); } @Override public boolean beforeComponentTypeRemoved(List<String> componentTypes) { return true; } @Override public void onComponentTypeRemoved(Map<String, String> componentTypes) { populateComponentTypes(formName); } @Override public void onResetDatabase() { populateComponentTypes(formName); } // ------------ Native methods ------------ /** * Take a Javascript function, embedded in an opaque JavaScriptObject, * and call it. * * @param callback the Javascript callback. */ private static native void doCallBack(JavaScriptObject callback, String buttonName) /*-{ callback.call(null, buttonName); }-*/; private static native void exportMethodsToJavascript() /*-{ $wnd.BlocklyPanel_initBlocksArea = $entry(@com.google.appinventor.client.editor.youngandroid.BlocklyPanel::initBlocksArea(Ljava/lang/String;)); $wnd.BlocklyPanel_blocklyWorkspaceChanged = $entry(@com.google.appinventor.client.editor.youngandroid.BlocklyPanel::blocklyWorkspaceChanged(Ljava/lang/String;)); $wnd.BlocklyPanel_switchLanguage = $entry(@com.google.appinventor.client.editor.youngandroid.BlocklyPanel::switchLanguage(Ljava/lang/String;Ljava/lang/String;)); $wnd.BlocklyPanel_checkWarningState = $entry(@com.google.appinventor.client.editor.youngandroid.BlocklyPanel::checkWarningState(Ljava/lang/String;)); $wnd.BlocklyPanel_callToggleWarning = $entry(@com.google.appinventor.client.editor.youngandroid.BlocklyPanel::callToggleWarning()); $wnd.BlocklyPanel_checkIsAdmin = $entry(@com.google.appinventor.client.editor.youngandroid.BlocklyPanel::checkIsAdmin()); $wnd.BlocklyPanel_indicateDisconnect = $entry(@com.google.appinventor.client.editor.youngandroid.BlocklyPanel::indicateDisconnect()); // Note: above lines are longer than 100 chars but I'm not sure whether they can be split $wnd.BlocklyPanel_pushScreen = $entry(@com.google.appinventor.client.editor.youngandroid.BlocklyPanel::pushScreen(Ljava/lang/String;)); $wnd.BlocklyPanel_popScreen = $entry(@com.google.appinventor.client.editor.youngandroid.BlocklyPanel::popScreen()); $wnd.BlocklyPanel_createDialog = $entry(@com.google.appinventor.client.editor.youngandroid.BlocklyPanel::createDialog(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILcom/google/gwt/core/client/JavaScriptObject;)); $wnd.BlocklyPanel_hideDialog = $entry(@com.google.appinventor.client.editor.youngandroid.BlocklyPanel::HideDialog(Lcom/google/gwt/user/client/ui/DialogBox;)); $wnd.BlocklyPanel_setDialogContent = $entry(@com.google.appinventor.client.editor.youngandroid.BlocklyPanel::SetDialogContent(Lcom/google/gwt/user/client/ui/DialogBox;Ljava/lang/String;)); $wnd.BlocklyPanel_getComponentInstanceTypeName = $entry(@com.google.appinventor.client.editor.youngandroid.BlocklyPanel::getComponentInstanceTypeName(Ljava/lang/String;Ljava/lang/String;)); $wnd.BlocklyPanel_getComponentInfo = $entry(@com.google.appinventor.client.editor.youngandroid.BlocklyPanel::getComponentInfo(Ljava/lang/String;)); $wnd.BlocklyPanel_getComponentsJSONString = $entry(@com.google.appinventor.client.editor.youngandroid.BlocklyPanel::getComponentsJSONString()); $wnd.BlocklyPanel_getYaVersion = $entry(@com.google.appinventor.client.editor.youngandroid.BlocklyPanel::getYaVersion()); $wnd.BlocklyPanel_getBlocksLanguageVersion = $entry(@com.google.appinventor.client.editor.youngandroid.BlocklyPanel::getBlocksLanguageVersion()); $wnd.BlocklyPanel_getLocalizedPropertyName = $entry(@com.google.appinventor.client.editor.youngandroid.BlocklyPanel::getLocalizedPropertyName(Ljava/lang/String;)); $wnd.BlocklyPanel_getLocalizedParameterName = $entry(@com.google.appinventor.client.editor.youngandroid.BlocklyPanel::getLocalizedParameterName(Ljava/lang/String;)); $wnd.BlocklyPanel_getLocalizedMethodName = $entry(@com.google.appinventor.client.editor.youngandroid.BlocklyPanel::getLocalizedMethodName(Ljava/lang/String;)); $wnd.BlocklyPanel_getLocalizedEventName = $entry(@com.google.appinventor.client.editor.youngandroid.BlocklyPanel::getLocalizedEventName(Ljava/lang/String;)); $wnd.BlocklyPanel_getLocalizedComponentType = $entry(@com.google.appinventor.client.editor.youngandroid.BlocklyPanel::getLocalizedComponentType(Ljava/lang/String;)); $wnd.BlocklyPanel_getBackpack = $entry(@com.google.appinventor.client.editor.youngandroid.BlocklyPanel::getBackpack()); $wnd.BlocklyPanel_setBackpack = $entry(@com.google.appinventor.client.editor.youngandroid.BlocklyPanel::setBackpack(Ljava/lang/String;)); }-*/; private native void initJS() /*-{ $wnd.myBlocklyPanel = this; $wnd.Blockly = null; // will be set to our iframe's Blockly object when // the iframe finishes loading }-*/; private static native void doAddComponent(String formName, String typeDescription, String instanceName, String uid) /*-{ $wnd.Blocklies[formName].Component.add(instanceName, uid); }-*/; private static native void doRemoveComponent(String formName, String typeName, String instanceName, String uid) /*-{ $wnd.Blocklies[formName].Component.remove(typeName, instanceName, uid); }-*/; private static native void doRenameComponent(String formName, String typeName, String oldName, String newName, String uid) /*-{ $wnd.Blocklies[formName].Component.rename(oldName, newName, uid) }-*/; private static native void doShowComponentBlocks(String formName, String name) /*-{ $wnd.Blocklies[formName].Drawer.showComponent(name); }-*/; public static native void doHideComponentBlocks(String formName) /*-{ $wnd.Blocklies[formName].Drawer.hide(); }-*/; private static native void doShowBuiltinBlocks(String formName, String drawerName) /*-{ var myBlockly = $wnd.Blocklies[formName]; myBlockly.Drawer.hide(); myBlockly.Drawer.showBuiltin(drawerName); }-*/; public static native void doHideBlocks(String formName) /*-{ $wnd.Blocklies[formName].Drawer.hide(); }-*/; private static native void doShowGenericBlocks(String formName, String drawerName) /*-{ var myBlockly = $wnd.Blocklies[formName]; myBlockly.Drawer.hide(); myBlockly.Drawer.showGeneric(drawerName); }-*/; public static native boolean doDrawerShowing(String formName) /*-{ return $wnd.Blocklies[formName].Drawer.isShowing(); }-*/; // [lyn, 2014/10/27] added formJson for upgrading public static native void doLoadBlocksContent(String formName, String formJson, String blocksContent) /*-{ $wnd.Blocklies[formName].SaveFile.load(formJson, blocksContent); }-*/; public static native String doGetBlocksContent(String formName) /*-{ return $wnd.Blocklies[formName].SaveFile.get(); }-*/; public static native String doGetYailRepl(String formName, String formJson, String packageName) /*-{ return $wnd.Blocklies[formName].Yail.getFormYail(formJson, packageName, true); }-*/; public static native String doGetYail(String formName, String formJson, String packageName) /*-{ return $wnd.Blocklies[formName].Yail.getFormYail(formJson, packageName); }-*/; public static native void doSendJson(String formName, String formJson, String packageName) /*-{ $wnd.Blocklies[formName].ReplMgr.sendFormData(formJson, packageName); }-*/; public static native void doResetYail(String formName) /*-{ $wnd.Blocklies[formName].ReplMgr.resetYail(); }-*/; public static native void doPollYail(String formName) /*-{ try { $wnd.Blocklies[formName].ReplMgr.pollYail(); } catch (e) { $wnd.console.log("doPollYail() Failed"); $wnd.console.log(e); } }-*/; public static native void doStartRepl(String formName, boolean alreadyRunning, boolean forEmulator, boolean forUsb) /*-{ $wnd.Blocklies[formName].ReplMgr.startRepl(alreadyRunning, forEmulator, forUsb); }-*/; public static native void doHardReset(String formName) /*-{ $wnd.Blocklies[formName].ReplMgr.ehardreset(formName); }-*/; public static native void doRenderBlockly(String formName) /*-{ $wnd.Blocklies[formName].BlocklyEditor.render(); }-*/; public static native void doToggleWarning(String formName) /*-{ $wnd.Blocklies[formName].WarningHandler.warningToggle(); }-*/; public static native void doCheckWarnings(String formName) /*-{ $wnd.Blocklies[formName].WarningHandler.checkAllBlocksForWarningsAndErrors(); }-*/; public static native String getCompVersion() /*-{ return $wnd.PREFERRED_COMPANION; }-*/; static native void setPreferredCompanion(String comp, String url) /*-{ $wnd.PREFERRED_COMPANION = comp; $wnd.COMPANION_UPDATE_URL = url; }-*/; static native void addAcceptableCompanionPackage(String comp) /*-{ $wnd.ACCEPTABLE_COMPANION_PACKAGE = comp; }-*/; static native void addAcceptableCompanion(String comp) /*-{ if ($wnd.ACCEPTABLE_COMPANIONS === null || $wnd.ACCEPTABLE_COMPANIONS === undefined) { $wnd.ACCEPTABLE_COMPANIONS = []; } $wnd.ACCEPTABLE_COMPANIONS.push(comp); }-*/; static native String doQRCode(String formName, String inString) /*-{ return $wnd.Blocklies[formName].ReplMgr.makeqrcode(inString); }-*/; /* * Switch the Blockly's language setting to "language" as specified in the * parameter argument. */ public static native void doSwitchLanguage(String formName, String language) /*-{ $wnd.Blocklies[formName].language_switch.switchLanguage(language); }-*/; public static native void doUpdateCompanion(String formName) /*-{ $wnd.Blocklies[formName].ReplMgr.triggerUpdate(); }-*/; public static native String getURL() /*-{ return $wnd.location.href; }-*/; /* * Update Component Types in Blockly ComponentTypes */ public static native void populateComponentTypes(String formName) /*-{ $wnd.Blocklies[formName].ComponentTypes.populateTypes(); }-*/; }