package collide.gwtc.view;
import xapi.inject.impl.SingletonProvider;
import xapi.log.X_Log;
import collide.client.common.CanRunApplication;
import collide.client.filetree.FileTreeController;
import collide.client.filetree.FileTreeModel;
import collide.client.filetree.FileTreeModelNetworkController.OutgoingController;
import collide.client.filetree.FileTreeNodeRenderer;
import collide.client.filetree.FileTreeNodeRenderer.Css;
import collide.client.util.Elements;
import collide.gwtc.GwtCompileStatus;
import collide.gwtc.GwtcController;
import collide.gwtc.resources.GwtcResources;
import com.google.collide.client.code.FileTreeSection;
import com.google.collide.client.code.debugging.DebuggingModelController;
import com.google.collide.client.code.debugging.DebuggingModelRenderer;
import com.google.collide.client.code.debugging.DebuggingSidebar;
import com.google.collide.client.code.debugging.EvaluationPopupController;
import com.google.collide.client.code.debugging.SourceMapping;
import com.google.collide.client.code.debugging.StaticSourceMapping;
import com.google.collide.client.communication.FrontendApi;
import com.google.collide.client.communication.FrontendApi.ApiCallback;
import com.google.collide.client.communication.MessageFilter;
import com.google.collide.client.communication.PushChannel;
import com.google.collide.client.communication.ResourceUriUtils;
import com.google.collide.client.history.Place;
import com.google.collide.client.history.PlaceNavigationEvent;
import com.google.collide.client.status.StatusManager;
import com.google.collide.client.ui.dropdown.DropdownWidgets;
import com.google.collide.client.ui.menu.PositionController.Position;
import com.google.collide.client.ui.menu.PositionController.PositionerBuilder;
import com.google.collide.client.ui.menu.PositionController.VerticalAlign;
import com.google.collide.client.ui.popup.Popup;
import com.google.collide.client.ui.tooltip.Tooltip;
import com.google.collide.client.util.PathUtil;
import com.google.collide.client.workspace.PopupBlockedInstructionalPopup;
import com.google.collide.dto.EmptyMessage;
import com.google.collide.dto.GetDirectory;
import com.google.collide.dto.GetDirectoryResponse;
import com.google.collide.dto.GetFileContents;
import com.google.collide.dto.GetFileContentsResponse;
import com.google.collide.dto.WorkspaceTreeUpdate;
import com.google.collide.json.client.JsoStringMap;
import com.google.collide.json.shared.JsonStringMap;
import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.Element;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.uibinder.client.UiTemplate;
import elemental.events.Event;
import elemental.events.EventListener;
import elemental.html.SpanElement;
import elemental.js.dom.JsElement;
public class GwtcModuleControlView {
public interface FileResources extends DropdownWidgets.Resources,
Tooltip.Resources,
FileTreeNodeRenderer.Resources,
FileTreeSection.Resources,
Popup.Resources,
DebuggingModelRenderer.Resources,
PopupBlockedInstructionalPopup.Resources,
EvaluationPopupController.Resources,
DebuggingSidebar.Resources
{
}
final FileResources resources = GWT.create(FileResources.class);
// This is a class that binds our ui.xml file to GwtcModuleControlView class
@UiTemplate("GwtcModuleControlView.ui.xml")
interface MyBinder extends UiBinder<com.google.gwt.dom.client.DivElement, GwtcModuleControlView> {}
// This is a generated instance of the above interface. It fills in this class's values.
static MyBinder binder = GWT.create(MyBinder.class);
// A provider for our resources; this object will create one and only one copy of resources,
// and make sure the css is injected into the hostpage before returning the resource provider.
private static final SingletonProvider<GwtcResources> resourceProvider = new SingletonProvider<GwtcResources>(){
protected GwtcResources initialValue() {
GwtcResources res = GWT.create(GwtcResources.class);
res.panelHeaderCss().ensureInjected();
return res;
};
};
/**
* Creates a new panel header with the given resources, all of which may be null.
*
* @param moduleContainer - The element in which to append the header.
* If null, you must attach the header somewhere.
* @param res - GwtcCss resource overrides. If null, our defaults are applied.
* @param model - A model for the panel, which contains values like isMaximizable, isClosable, etc.
*
* @return - A GwtcModuleControlView widget.
*/
public static GwtcModuleControlView create(GwtcController controller) {
return new GwtcModuleControlView(null, controller);
}
// The elements from our .ui.xml file. These are filled in by the compiler.
@UiField public Element header;
@UiField Element headerContainer;
@UiField Element controls;
@UiField Element close;
@UiField Element icons;
@UiField Element reload;
@UiField Element status;
@UiField Element generated;
// This is our css data; the compiler uses this object when filling in values.
@UiField(provided=true) final GwtcResources res;
private GwtCompileStatus currentStatus = GwtCompileStatus.Pending;
public GwtcModuleControlView(GwtcResources res, final GwtcController controller) {
// Allow users to override default css.
this.res = res == null ? resourceProvider.get() : res;
// Calls into the generated ui binder, creating html elements and filling in our values.
binder.createAndBindUi(this);
Css css = this.res.workspaceNavigationFileTreeNodeRendererCss();
JsoStringMap<String> map = FileTreeNodeRenderer.createFileTypeMap(css);
SpanElement contents = FileTreeNodeRenderer.renderNodeContents(css, "Generated", false, true, map, new EventListener() {
@Override
public void handleEvent(Event evt) {
FileTreeSection files = testFileTree();
generated.<JsElement>cast().appendChild(files.getView().getElement());
files.getTree().renderTree(0);
Popup popup = Popup.create(resources);
popup.addPartner(generated.<JsElement>cast());
popup.setContentElement(files.getView().getElement());
popup.show(new PositionerBuilder()
.setVerticalAlign(VerticalAlign.BOTTOM)
.setPosition(Position.NO_OVERLAP)
.buildAnchorPositioner(generated.<JsElement>cast()));
}
}, true);
Elements.asJsElement(generated).appendChild(contents);
reload.<JsElement>cast().setOnclick(new EventListener() {
@Override
public void handleEvent(Event evt) {
controller.onRefreshClicked();
}
});
close.<JsElement>cast().setOnclick(new EventListener() {
@Override
public void handleEvent(Event evt) {
controller.onCloseClicked();
}
});
status.<JsElement>cast().setOnclick(new EventListener() {
@Override
public void handleEvent(Event evt) {
if (currentStatus == null) {
controller.onReloadClicked();
return;
}
switch (currentStatus) {
case PartialSuccess:
case Pending:
case Success:
case Fail:
controller.onReloadClicked();
default:
// TODO warn that a compile is in progress
}
}
});
}
public elemental.dom.Element getElement() {
return (elemental.dom.Element)headerContainer;
}
public void setCompileStatus(GwtCompileStatus compileStatus) {
assert compileStatus != null;
X_Log.info("Setting compile status", compileStatus.name(), this);
if (currentStatus == compileStatus) {
return;
}
status.removeClassName(classFor(currentStatus));
status.addClassName(classFor(compileStatus));
currentStatus = compileStatus;
}
private String classFor(GwtCompileStatus status) {
switch (status) {
case Pending:
return res.panelHeaderCss().gear();
case Success:
return res.panelHeaderCss().success();
case PartialSuccess:
return res.panelHeaderCss().warn();
case Good:
return res.panelHeaderCss().radarGreen();
case Warn:
return res.panelHeaderCss().radarYellow();
case Error:
return res.panelHeaderCss().radarRed();
case Fail:
return res.panelHeaderCss().fail();
}
throw new IllegalStateException();
}
public void setHeader(String id) {
header.setInnerHTML(id);
}
private FileTreeSection testFileTree() {
Place place = new Place("Generated") {
@Override
public PlaceNavigationEvent<? extends Place> createNavigationEvent(
final JsonStringMap<String> decodedState) {
return new PlaceNavigationEvent<Place>(this) {
@Override
public JsonStringMap<String> getBookmarkableState() {
return decodedState;
}
};
}
};
FileTreeController<?> fileTreeController = new FileTreeController<FileResources>() {
protected StatusManager statusManager;
protected MessageFilter messageFilter;
protected PushChannel pushChannel;
protected FrontendApi frontendApi;
{
this.statusManager = new StatusManager();
this.messageFilter = new MessageFilter();
// Things that depend on message filter/frontendApi/statusManager
this.pushChannel = PushChannel.create(messageFilter, statusManager);
this.frontendApi = FrontendApi.create(pushChannel, statusManager);
}
@Override
public FileResources getResources() {
return resources;
}
@Override
public void mutateWorkspaceTree(WorkspaceTreeUpdate msg, ApiCallback<EmptyMessage> callback) {
frontendApi.MUTATE_WORKSPACE_TREE.send(msg, callback);
}
@Override
public void getFileContents(GetFileContents getFileContents,
ApiCallback<GetFileContentsResponse> callback) {
frontendApi.GET_FILE_CONTENTS.send(getFileContents, callback);
}
@Override
public StatusManager getStatusManager() {
return statusManager;
}
@Override
public void getDirectory(GetDirectory getDirectoryAndPath,
ApiCallback<GetDirectoryResponse> callback) {
frontendApi.GET_DIRECTORY.send(getDirectoryAndPath, callback);
}
@Override
public MessageFilter getMessageFilter() {
return messageFilter;
}
};
OutgoingController networkController = new OutgoingController(fileTreeController);
FileTreeModel fileTreeModel = new FileTreeModel(networkController);
CanRunApplication runner = new CanRunApplication() {
@Override
public boolean runApplication(PathUtil applicationPath) {
String baseUri = ResourceUriUtils.getAbsoluteResourceBaseUri();
SourceMapping sourceMapping = StaticSourceMapping.create(baseUri);
elemental.html.Window popup = DebuggingModelController.createOrOpenPopup(resources);
if (popup != null) {
String absoluteResourceUri = sourceMapping.getRemoteSourceUri(applicationPath);
popup.getLocation().assign(absoluteResourceUri);
}
return false;
}
};
return FileTreeSection.create(place, fileTreeController, fileTreeModel, runner);
}
}