// -*- 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.explorer.commands; import com.google.appinventor.client.Ode; import static com.google.appinventor.client.Ode.MESSAGES; import com.google.appinventor.client.OdeAsyncCallback; import com.google.appinventor.client.explorer.project.Project; import com.google.appinventor.client.explorer.project.ProjectComparators; import com.google.appinventor.client.widgets.LabeledTextBox; import com.google.appinventor.client.youngandroid.TextValidators; import com.google.appinventor.shared.rpc.project.ProjectNode; import com.google.appinventor.shared.rpc.project.ProjectRootNode; import com.google.appinventor.shared.rpc.project.UserProject; import com.google.appinventor.client.widgets.Validator; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; import com.google.gwt.event.dom.client.KeyCodes; import com.google.gwt.event.dom.client.KeyUpEvent; import com.google.gwt.event.dom.client.KeyUpHandler; import com.google.gwt.i18n.client.DateTimeFormat; import com.google.gwt.user.client.Command; import com.google.gwt.user.client.DeferredCommand; import com.google.gwt.user.client.ui.Button; import com.google.gwt.user.client.ui.DialogBox; import com.google.gwt.user.client.ui.Grid; import com.google.gwt.user.client.ui.HorizontalPanel; import com.google.gwt.user.client.ui.Label; import com.google.gwt.user.client.ui.ScrollPanel; import com.google.gwt.user.client.ui.VerticalPanel; import com.google.gwt.user.client.ui.Widget; import java.util.Collections; import java.util.Date; import java.util.List; /** * A command that brings up a wizard to copy a Young Android project. * * @author lizlooney@google.com (Liz Looney) */ public final class CopyYoungAndroidProjectCommand extends ChainableCommand { private boolean checkpoint; /** * Creates a new command for copying a project. * * @param checkpoint whether the copy is a checkpoint */ public CopyYoungAndroidProjectCommand(boolean checkpoint) { this.checkpoint = checkpoint; } @Override public boolean willCallExecuteNextCommand() { return false; // Is there a way to do this for this command? } @Override public void execute(final ProjectNode node) { new CopyProjectDialog(node.getProjectRoot()).center(); } /** * Dialog for getting the new name for saving. */ private class CopyProjectDialog extends DialogBox { // UI elements private final LabeledTextBox newNameTextBox; /** * Creates a new dialog to get new project name. * * @param oldProjectNode old project to copy */ public CopyProjectDialog(final ProjectRootNode oldProjectNode) { super(false, true); setStylePrimaryName("ode-DialogBox"); String oldName = oldProjectNode.getName(); setText(checkpoint ? MESSAGES.checkpointTitle(oldName) : MESSAGES.saveAsTitle(oldName)); VerticalPanel contentPanel = new VerticalPanel(); contentPanel.setSpacing(10); String defaultNewName; if (checkpoint) { String prefix = MESSAGES.defaultCheckpointProjectName(oldName, ""); List<Project> checkpointProjects = Ode.getInstance().getProjectManager().getProjects(prefix); String nextSuffix; if (checkpointProjects.isEmpty()) { nextSuffix = "1"; } else { // Sort the checkpoints project by the date they were last modified, in descending order. Collections.sort(checkpointProjects, ProjectComparators.COMPARE_BY_DATE_MODIFIED_DESCENDING); VerticalPanel previousCheckpointsPanel = new VerticalPanel(); previousCheckpointsPanel.setSpacing(0); previousCheckpointsPanel.add(new Label(MESSAGES.previousCheckpointsLabel())); Widget previousCheckpointsTable = createPreviousCheckpointsTable(checkpointProjects); previousCheckpointsTable.setSize("100%", "100%"); ScrollPanel scrollPanel = new ScrollPanel(previousCheckpointsTable); scrollPanel.addStyleName("ode-CheckpointProjectTable"); scrollPanel.setWidth("100%"); if (checkpointProjects.size() > 5) { scrollPanel.setHeight("121px"); } previousCheckpointsPanel.add(scrollPanel); previousCheckpointsPanel.setWidth("100%"); contentPanel.add(previousCheckpointsPanel); // Find the highest number in the checkpoint projects' names. int highestNumber = 0; int prefixLength = prefix.length(); for (Project checkpointProject : checkpointProjects) { String checkpointName = checkpointProject.getProjectName(); try { highestNumber = Math.max(highestNumber, Integer.parseInt(checkpointName.substring(prefixLength))); } catch (NumberFormatException e) { continue; } } nextSuffix = Integer.toString(highestNumber + 1); } defaultNewName = MESSAGES.defaultCheckpointProjectName(oldName, nextSuffix); } else { // Save As defaultNewName = MESSAGES.defaultSaveAsProjectName(oldName); } newNameTextBox = new LabeledTextBox(checkpoint ? MESSAGES.checkpointNameLabel() : MESSAGES.newNameLabel(), new Validator(){ @Override public boolean validate(String value) { errorMessage = TextValidators.getErrorMessage(value); return !(errorMessage.length()>0); } @Override public String getErrorMessage() { return errorMessage; } }); newNameTextBox.setText(defaultNewName); newNameTextBox.getTextBox().addKeyUpHandler(new KeyUpHandler() { @Override public void onKeyUp(KeyUpEvent event) { int keyCode = event.getNativeKeyCode(); if (keyCode == KeyCodes.KEY_ENTER) { handleOkClick(oldProjectNode); } else if (keyCode == KeyCodes.KEY_ESCAPE) { hide(); } else { newNameTextBox.validate(); } } }); contentPanel.add(newNameTextBox); HorizontalPanel buttonPanel = new HorizontalPanel(); Button cancelButton = new Button(MESSAGES.cancelButton()); cancelButton.addClickHandler(new ClickHandler() { @Override public void onClick(ClickEvent event) { hide(); } }); buttonPanel.add(cancelButton); Button okButton = new Button(MESSAGES.okButton()); okButton.addClickHandler(new ClickHandler() { @Override public void onClick(ClickEvent event) { handleOkClick(oldProjectNode); } }); buttonPanel.add(okButton); buttonPanel.setSize("100%", "24px"); contentPanel.add(buttonPanel); contentPanel.setSize(checkpoint ? "400px" : "320px", "100%"); add(contentPanel); } private void handleOkClick(ProjectRootNode oldProjectNode) { String newProjectName = newNameTextBox.getText(); if (TextValidators.checkNewProjectName(newProjectName)) { hide(); copyProjectAction(oldProjectNode, newProjectName); } else { newNameTextBox.setFocus(true); newNameTextBox.selectAll(); } } private Widget createPreviousCheckpointsTable(List<Project> checkpointProjects) { Grid table = new Grid(1 + checkpointProjects.size(), 3); table.addStyleName("ode-ProjectTable"); // Set the widgets for the header row. table.getRowFormatter().setStyleName(0, "ode-ProjectHeaderRow"); table.setWidget(0, 0, new Label(MESSAGES.projectNameHeader())); table.setWidget(0, 1, new Label(MESSAGES.projectDateCreatedHeader())); table.setWidget(0, 2, new Label(MESSAGES.projectDateModifiedHeader())); // Set the widgets for the rows representing previous checkpoints DateTimeFormat dateTimeFormat = DateTimeFormat.getMediumDateTimeFormat(); int row = 1; for (Project checkpointProject : checkpointProjects) { table.getRowFormatter().setStyleName(row, "ode-ProjectRowUnHighlighted"); Label nameLabel = new Label(checkpointProject.getProjectName()); table.setWidget(row, 0, nameLabel); Date dateCreated = new Date(checkpointProject.getDateCreated()); table.setWidget(row, 1, new Label(dateTimeFormat.format(dateCreated))); Date dateModified = new Date(checkpointProject.getDateModified()); table.setWidget(row, 2, new Label(dateTimeFormat.format(dateModified))); row++; } return table; } /** * Copies a project and gives it a new name. * * @param newName the new project name */ protected void copyProjectAction(ProjectRootNode oldProjectNode, String newName) { final Ode ode = Ode.getInstance(); OdeAsyncCallback<UserProject> callback = new OdeAsyncCallback<UserProject>( // failure message MESSAGES.copyProjectError()) { @Override public void onSuccess(UserProject newProjectInfo) { // Update project list Project newProject = ode.getProjectManager().addProject(newProjectInfo); if (!checkpoint) { ode.openYoungAndroidProjectInDesigner(newProject); } } }; // Create new copy on the backend ode.getProjectService().copyProject(oldProjectNode.getProjectId(), newName, callback); } @Override public void show() { super.show(); DeferredCommand.addCommand(new Command() { @Override public void execute() { newNameTextBox.setFocus(true); newNameTextBox.selectAll(); } }); } } }