diff --git a/appinventor/appengine/src/com/google/appinventor/client/OdeMessages.java b/appinventor/appengine/src/com/google/appinventor/client/OdeMessages.java
index 4fe7969385e..66220ae64db 100755
--- a/appinventor/appengine/src/com/google/appinventor/client/OdeMessages.java
+++ b/appinventor/appengine/src/com/google/appinventor/client/OdeMessages.java
@@ -4897,6 +4897,10 @@ String newerVersionComponentException(String componentType, int srcCompVersion,
@Description("")
String createWelcomeDialogButton();
+ @DefaultMessage("Loading screen {0} ({1} seconds estimated remaining)...")
+ @Description("")
+ String loadingScreen(String screenName, int secondsRemaining);
+
@DefaultMessage("Do Not Show Again")
@Description("")
String doNotShow();
@@ -4992,6 +4996,18 @@ String newerVersionComponentException(String componentType, int srcCompVersion,
@Description("")
String corruptionDialogText();
+ @DefaultMessage("Project {0} is missing Screen1. Refusing to continue loading the project.")
+ @Description("")
+ String screen1MissingText(String projectName);
+
+ @DefaultMessage("Project {0} is missing a designer file for screen {1}.")
+ @Description("")
+ String screenMissingDesigner(String projectName, String formName);
+
+ @DefaultMessage("Project {0} is missing a blocks file for screen {1}.")
+ @Description("")
+ String screenMissingBlocks(String projectName, String formName);
+
@DefaultMessage("
We detected errors while reading in your project
" +
"To protect your project from damage, we have ended this session. You may close this " +
"window.
")
diff --git a/appinventor/appengine/src/com/google/appinventor/client/editor/FileEditor.java b/appinventor/appengine/src/com/google/appinventor/client/editor/FileEditor.java
index 95daf84e404..90e42579b59 100644
--- a/appinventor/appengine/src/com/google/appinventor/client/editor/FileEditor.java
+++ b/appinventor/appengine/src/com/google/appinventor/client/editor/FileEditor.java
@@ -1,13 +1,12 @@
// -*- mode: java; c-basic-offset: 2; -*-
// Copyright 2009-2011 Google, All Rights reserved
-// Copyright 2011-2012 MIT, All rights reserved
+// Copyright 2011-2019 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;
import com.google.appinventor.client.Ode;
-import com.google.appinventor.client.editor.simple.palette.DropTargetProvider;
import com.google.appinventor.shared.rpc.project.FileNode;
import com.google.appinventor.shared.rpc.project.ProjectRootNode;
import com.google.gwt.core.client.Callback;
diff --git a/appinventor/appengine/src/com/google/appinventor/client/editor/youngandroid/DesignToolbar.java b/appinventor/appengine/src/com/google/appinventor/client/editor/youngandroid/DesignToolbar.java
index efb877c57cf..eb4d53ca626 100644
--- a/appinventor/appengine/src/com/google/appinventor/client/editor/youngandroid/DesignToolbar.java
+++ b/appinventor/appengine/src/com/google/appinventor/client/editor/youngandroid/DesignToolbar.java
@@ -1,6 +1,6 @@
// -*- mode: java; c-basic-offset: 2; -*-
// Copyright 2009-2011 Google, All Rights reserved
-// Copyright 2011-2012 MIT, All rights reserved
+// Copyright 2011-2019 MIT, All rights reserved
// Released under the Apache License, Version 2.0
// http://www.apache.org/licenses/LICENSE-2.0
@@ -8,6 +8,8 @@
import com.google.appinventor.client.ErrorReporter;
import com.google.appinventor.client.Ode;
+import static com.google.appinventor.client.Ode.MESSAGES;
+
import com.google.appinventor.client.editor.FileEditor;
import com.google.appinventor.client.editor.ProjectEditor;
import com.google.appinventor.client.editor.youngandroid.actions.SwitchScreenAction;
@@ -29,8 +31,6 @@
import java.util.Map;
import java.util.logging.Logger;
-import static com.google.appinventor.client.Ode.MESSAGES;
-
/**
* The design toolbar houses command buttons in the Young Android Design
* tab (for the UI designer (a.k.a, Form Editor) and Blocks Editor).
@@ -70,7 +70,7 @@ public static class DesignProject {
public DesignProject(String name, long projectId) {
this.name = name;
this.projectId = projectId;
- screens = Maps.newHashMap();
+ screens = Maps.newTreeMap();
// Screen1 is initial screen by default
currentScreen = YoungAndroidSourceNode.SCREEN1_FORM_NAME;
// Let BlocklyPanel know which screen to send Yail for
@@ -245,16 +245,10 @@ private boolean switchToProject(long projectId, String projectName) {
return true;
}
pushedScreens.clear(); // Effectively switching applications; clear stack of screens.
- clearDropDownMenu(WIDGET_NAME_SCREENS_DROPDOWN);
LOG.info("DesignToolbar: switching to existing project " + projectName + " with id "
+ projectId);
currentProject = project;
-
- // TODO(sharon): add screens to drop-down menu in the right order
- for (Screen screen : currentProject.screens.values()) {
- addDropDownButtonItem(WIDGET_NAME_SCREENS_DROPDOWN, new DropDownItem(screen.screenName,
- screen.screenName, new SwitchScreenAction(projectId, screen.screenName)));
- }
+ sortScreenList(projectId);
projectNameLabel.setText(projectName);
YaBlocksEditor.resendAssetsAndExtensions(); // Send assets for active project
} else {
@@ -267,6 +261,17 @@ private boolean switchToProject(long projectId, String projectName) {
return true;
}
+ public void sortScreenList(long projectId) {
+ if (currentProject != null) { // We can only sort screens if we have a project.
+ clearDropDownMenu(WIDGET_NAME_SCREENS_DROPDOWN);
+ // TODO(sharon): add screens to drop-down menu in the right order
+ for (Screen screen : currentProject.screens.values()) {
+ addDropDownButtonItem(WIDGET_NAME_SCREENS_DROPDOWN, new DropDownItem(screen.screenName,
+ screen.screenName, new SwitchScreenAction(projectId, screen.screenName)));
+ }
+ }
+ }
+
/*
* Add a screen name to the drop-down for the project with id projectId.
* name is the form name, formEditor is the file editor for the form UI,
@@ -282,8 +287,7 @@ public void addScreen(long projectId, String name, FileEditor formEditor,
DesignProject project = projectMap.get(projectId);
if (project.addScreen(name, formEditor, blocksEditor)) {
if (currentProject == project) {
- addDropDownButtonItem(WIDGET_NAME_SCREENS_DROPDOWN, new DropDownItem(name,
- name, new SwitchScreenAction(projectId, name)));
+ sortScreenList(projectId);
}
}
}
diff --git a/appinventor/appengine/src/com/google/appinventor/client/editor/youngandroid/YaFormEditor.java b/appinventor/appengine/src/com/google/appinventor/client/editor/youngandroid/YaFormEditor.java
index 203e9e593b9..e82711f8955 100644
--- a/appinventor/appengine/src/com/google/appinventor/client/editor/youngandroid/YaFormEditor.java
+++ b/appinventor/appengine/src/com/google/appinventor/client/editor/youngandroid/YaFormEditor.java
@@ -212,7 +212,7 @@ public void loadFile(final Command afterFileLoaded) {
final String fileId = getFileId();
OdeAsyncCallback callback = new OdeAsyncCallback(MESSAGES.loadError()) {
@Override
- public void onSuccess(ChecksumedLoadFile result) {
+ public void onSuccess(final ChecksumedLoadFile result) {
String contents;
try {
contents = result.getContent();
@@ -229,6 +229,11 @@ public void execute() {
} catch(IllegalArgumentException e) {
return;
}
+ try {
+ result.setContent(fileContentHolder.getFileContent());
+ } catch (ChecksumedFileException e) {
+ LOG.warning("ChecksumedFileException in YaFormEditor");
+ }
if (afterFileLoaded != null) {
afterFileLoaded.execute();
}
diff --git a/appinventor/appengine/src/com/google/appinventor/client/editor/youngandroid/YaProjectEditor.java b/appinventor/appengine/src/com/google/appinventor/client/editor/youngandroid/YaProjectEditor.java
index 34e708c0123..748e8e52db8 100644
--- a/appinventor/appengine/src/com/google/appinventor/client/editor/youngandroid/YaProjectEditor.java
+++ b/appinventor/appengine/src/com/google/appinventor/client/editor/youngandroid/YaProjectEditor.java
@@ -20,6 +20,8 @@
import com.google.appinventor.client.editor.simple.components.MockFusionTablesControl;
import com.google.appinventor.client.editor.simple.components.MockTwitter;
import com.google.appinventor.client.explorer.dialogs.ProjectPropertiesDialogBox;
+import com.google.appinventor.client.editor.youngandroid.i18n.BlocklyMsg;
+import com.google.appinventor.client.explorer.dialogs.ProgressBarDialogBox;
import com.google.appinventor.client.explorer.project.ComponentDatabaseChangeListener;
import com.google.appinventor.client.explorer.project.Project;
import com.google.appinventor.client.explorer.project.ProjectChangeListener;
@@ -33,6 +35,7 @@
import com.google.appinventor.shared.rpc.project.ChecksumedLoadFile;
import com.google.appinventor.shared.rpc.project.ProjectNode;
import com.google.appinventor.shared.rpc.project.ProjectRootNode;
+import com.google.appinventor.shared.rpc.project.SourceNode;
import com.google.appinventor.shared.rpc.project.youngandroid.YoungAndroidBlocksNode;
import com.google.appinventor.shared.rpc.project.youngandroid.YoungAndroidComponentsFolder;
import com.google.appinventor.shared.rpc.project.youngandroid.YoungAndroidFormNode;
@@ -52,8 +55,11 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
+import java.util.Deque;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -87,6 +93,8 @@ interface CombinedUi extends UiBinder {}
private static class EditorSet {
YaFormEditor formEditor = null;
YaBlocksEditor blocksEditor = null;
+ YoungAndroidFormNode formNode = null;
+ YoungAndroidBlocksNode blocksNode = null;
}
// Maps form name -> editors for this form
@@ -103,6 +111,8 @@ private static class EditorSet {
// Database of component type descriptions
private final SimpleComponentDatabase COMPONENT_DATABASE;
+ private final Deque loadingEditors = new LinkedList<>();
+
// State variables to help determine whether we are ready to show Screen1
// Automatically select the Screen1 form editor when we have finished loading
// both the form and blocks editors for Screen1 and we have added the
@@ -147,32 +157,38 @@ public YaProjectEditor(ProjectRootNode projectRootNode) {
COMPONENT_DATABASE = SimpleComponentDatabase.getInstance(projectId);
}
- private void loadBlocksEditor(String formNamePassedIn) {
+ private void loadBlocksEditor(String formNamePassedIn, final Runnable callback) {
final String formName = formNamePassedIn;
- final YaBlocksEditor newBlocksEditor = editorMap.get(formName).blocksEditor;
+ final EditorSet editors = editorMap.get(formName);
+ final YaBlocksEditor newBlocksEditor = editors.blocksEditor;
newBlocksEditor.loadFile(new Command() {
- @Override
- public void execute() {
- YaBlocksEditor newBlocksEditor = editorMap.get(formName).blocksEditor;
- int pos = Collections.binarySearch(fileIds, newBlocksEditor.getFileId(),
- getFileIdComparator());
- if (pos < 0) {
- pos = -pos - 1;
- }
- insertFileEditor(newBlocksEditor, pos);
- if (isScreen1(formName)) {
- screen1BlocksLoaded = true;
- if (readyToShowScreen1()) {
- LOG.info("YaProjectEditor.addBlocksEditor.loadFile.execute: switching to screen "
- + formName + " for project " + newBlocksEditor.getProjectId());
- Ode.getInstance().getDesignToolbar().switchToScreen(newBlocksEditor.getProjectId(),
- formName, DesignToolbar.View.FORM);
- }
+ @Override
+ public void execute() {
+ final EditorSet editors = editorMap.get(formName);
+ YaBlocksEditor newBlocksEditor = editors.blocksEditor;
+ LOG.info("Loaded " + formName + ".bky");
+ loadingEditors.remove(editors);
+ int pos = Collections.binarySearch(fileIds, newBlocksEditor.getFileId(),
+ getFileIdComparator());
+ if (pos < 0) {
+ pos = -pos - 1;
+ }
+ insertFileEditor(newBlocksEditor, pos);
+ if (isScreen1(formName)) {
+ screen1BlocksLoaded = true;
+ if (readyToShowScreen1()) {
+ LOG.info("YaProjectEditor.addBlocksEditor.loadFile.execute: switching to screen "
+ + formName + " for project " + newBlocksEditor.getProjectId());
+ Ode.getInstance().getDesignToolbar().switchToScreen(newBlocksEditor.getProjectId(),
+ formName, DesignToolbar.View.FORM);
}
}
- });
-
+ if (callback != null) {
+ callback.run();
+ }
+ }
+ });
}
/**
@@ -187,47 +203,107 @@ public void processProject() {
.then(this::loadProject);
}
+ private EditorSet getOrCreateEditorSet(String formName) {
+ EditorSet result = editorMap.get(formName);
+ if (result == null) {
+ result = new EditorSet();
+ editorMap.put(formName, result);
+ }
+ return result;
+ }
+
// Note: When we add the blocks editors in the loop below we do not actually
// have them load the blocks file. Instead we trigger the load of a blocks file
// in the callback for the loading of its associated forms file. This is important
// because we have to ensure that the component type data is available when the
// blocks are loaded!
-
private Promise