In progress: [Issue 215] Add a "new .proto file" wizard

Added "New File" wizard generated by Eclipse.
diff --git a/com.google.eclipse.protobuf.ui/plugin.xml b/com.google.eclipse.protobuf.ui/plugin.xml
index 691a6d6..d8cb788 100644
--- a/com.google.eclipse.protobuf.ui/plugin.xml
+++ b/com.google.eclipse.protobuf.ui/plugin.xml
@@ -298,4 +298,18 @@
       </command>
     </menuContribution>
   </extension>
+  <extension
+        point="org.eclipse.ui.newWizards">
+     <category
+           id="com.google.eclipse.protobuf.ui"
+           name="Protocol Buffer">
+     </category>
+     <wizard
+           category="com.google.eclipse.protobuf.ui"
+           class="com.google.eclipse.protobuf.ui.wizards.NewProtoFileWizard"
+           icon="icons/pb.gif"
+           id="com.google.eclipse.protobuf.ui.wizards.NewProtoFileWizard"
+           name="Protocol Buffer Descriptor File">
+     </wizard>
+  </extension>
 </plugin>
diff --git a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/ProtobufUiModule.java b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/ProtobufUiModule.java
index e01066f..003ef12 100644
--- a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/ProtobufUiModule.java
+++ b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/ProtobufUiModule.java
@@ -60,8 +60,6 @@
  * @author alruiz@google.com (Alex Ruiz)
  */
 public class ProtobufUiModule extends AbstractProtobufUiModule {
-  public static final String PLUGIN_ID = "com.google.eclipse.protobuf.ui";
-
   public ProtobufUiModule(AbstractUIPlugin plugin) {
     super(plugin);
     setValidationTrigger(activeWorkbenchWindow(), plugin);
diff --git a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/builder/protoc/ProtobufBuildParticipant.java b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/builder/protoc/ProtobufBuildParticipant.java
index d8e074f..2c356e5 100644
--- a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/builder/protoc/ProtobufBuildParticipant.java
+++ b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/builder/protoc/ProtobufBuildParticipant.java
@@ -10,8 +10,8 @@
 
 import static com.google.common.io.Closeables.closeQuietly;
 import static com.google.eclipse.protobuf.ui.builder.protoc.ConsolePrinter.createAndDisplayConsole;
-import static com.google.eclipse.protobuf.ui.exception.CoreExceptions.error;
 import static com.google.eclipse.protobuf.ui.preferences.compiler.CompilerPreferences.compilerPreferences;
+import static com.google.eclipse.protobuf.ui.util.IStatusFactory.error;
 import static com.google.eclipse.protobuf.ui.util.Workspaces.workspaceRoot;
 import static org.eclipse.core.resources.IResource.DEPTH_INFINITE;
 
@@ -91,7 +91,7 @@
       process.destroy();
     } catch (Throwable e) {
       e.printStackTrace();
-      throw error(e);
+      throw new CoreException(error(e));
     } finally {
       if (console != null) {
         console.close();
diff --git a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/editor/model/FileStoreDocumentContentsFactory.java b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/editor/model/FileStoreDocumentContentsFactory.java
index 9cab376..fd5442d 100644
--- a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/editor/model/FileStoreDocumentContentsFactory.java
+++ b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/editor/model/FileStoreDocumentContentsFactory.java
@@ -9,10 +9,9 @@
 package com.google.eclipse.protobuf.ui.editor.model;
 
 import static com.google.common.io.Closeables.closeQuietly;
-import static com.google.eclipse.protobuf.ui.exception.CoreExceptions.error;
+import static com.google.eclipse.protobuf.ui.util.IStatusFactory.error;
 
-import com.google.eclipse.protobuf.ui.resource.XtextResourceFactory;
-import com.google.inject.Inject;
+import java.io.*;
 
 import org.eclipse.core.runtime.CoreException;
 import org.eclipse.ui.IEditorInput;
@@ -20,7 +19,8 @@
 import org.eclipse.xtext.resource.XtextResource;
 import org.eclipse.xtext.ui.editor.model.XtextDocument;
 
-import java.io.*;
+import com.google.eclipse.protobuf.ui.resource.XtextResourceFactory;
+import com.google.inject.Inject;
 
 /**
  * @author alruiz@google.com (Alex Ruiz)
@@ -38,7 +38,7 @@
       XtextResource resource = resourceFactory.createResource(file.toURI().toString(), contents);
       document.setInput(resource);
     } catch (Throwable t) {
-      throw error(t);
+      throw new CoreException(error(t));
     }
   }
 
diff --git a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/editor/model/ProtobufDocumentProvider.java b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/editor/model/ProtobufDocumentProvider.java
index 9461248..5c53b9f 100644
--- a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/editor/model/ProtobufDocumentProvider.java
+++ b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/editor/model/ProtobufDocumentProvider.java
@@ -10,7 +10,7 @@
 package com.google.eclipse.protobuf.ui.editor.model;
 
 import static com.google.common.collect.Lists.newArrayList;
-import static com.google.eclipse.protobuf.ui.exception.CoreExceptions.error;
+import static com.google.eclipse.protobuf.ui.util.IStatusFactory.error;
 import static com.google.eclipse.protobuf.util.Encodings.UTF_8;
 import static org.eclipse.core.filebuffers.FileBuffers.getTextFileBufferManager;
 import static org.eclipse.core.filebuffers.LocationKind.*;
@@ -119,7 +119,7 @@
       edit.apply(document);
       manager.endCompoundChange();
     } catch (Throwable t) {
-      throw error(t);
+      throw new CoreException(error(t));
     }
   }
 
diff --git a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/editor/model/UriDocumentContentsFactory.java b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/editor/model/UriDocumentContentsFactory.java
index 7ddc700..aa34426 100644
--- a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/editor/model/UriDocumentContentsFactory.java
+++ b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/editor/model/UriDocumentContentsFactory.java
@@ -9,10 +9,9 @@
 package com.google.eclipse.protobuf.ui.editor.model;
 
 import static com.google.common.io.Closeables.closeQuietly;
-import static com.google.eclipse.protobuf.ui.exception.CoreExceptions.error;
+import static com.google.eclipse.protobuf.ui.util.IStatusFactory.error;
 
-import java.io.IOException;
-import java.io.InputStream;
+import java.io.*;
 import java.net.URL;
 
 import org.eclipse.core.runtime.CoreException;
@@ -41,7 +40,7 @@
       XtextResource resource = resourceFactory.createResource(uri, contents);
       document.setInput(resource);
     } catch (Throwable t) {
-      throw error(t);
+      throw new CoreException(error(t));
     }
   }
 
diff --git a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/exception/CoreExceptions.java b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/exception/CoreExceptions.java
deleted file mode 100644
index 2c7f03d..0000000
--- a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/exception/CoreExceptions.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright (c) 2011 Google Inc.
- *
- * All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse
- * Public License v1.0 which accompanies this distribution, and is available at
- *
- * http://www.eclipse.org/legal/epl-v10.html
- */
-package com.google.eclipse.protobuf.ui.exception;
-
-import static com.google.common.base.Strings.nullToEmpty;
-import static com.google.eclipse.protobuf.ui.ProtobufUiModule.PLUGIN_ID;
-import static org.eclipse.core.runtime.IStatus.ERROR;
-
-import org.eclipse.core.runtime.*;
-
-/**
- * Factory of <code>{@link CoreException}</code>s.
- *
- * @author alruiz@google.com (Alex Ruiz)
- */
-public final class CoreExceptions {
-  public static CoreException error(Throwable cause) {
-    String message = nullToEmpty(cause.getMessage());
-    return new CoreException(new Status(ERROR, PLUGIN_ID, message, cause));
-  }
-
-  private CoreExceptions() {}
-}
diff --git a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/plugin/ProtobufEditorPlugIn.java b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/plugin/ProtobufEditorPlugIn.java
index 2062240..d9b4928 100644
--- a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/plugin/ProtobufEditorPlugIn.java
+++ b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/plugin/ProtobufEditorPlugIn.java
@@ -15,6 +15,7 @@
  * @author alruiz@google.com (Alex Ruiz)
  */
 public final class ProtobufEditorPlugIn {
+  private static final String PLUGIN_ID = "com.google.eclipse.protobuf.ui";
   private static final String LANGUAGE_NAME = "com.google.eclipse.protobuf.Protobuf";
 
   /**
@@ -34,13 +35,13 @@
     return ProtobufActivator.getInstance().getInjector(protobufLanguageName());
   }
 
-  /**
-   * Returns the name of the 'Protocol Buffer' language.
-   * @return the name of the 'Protocol Buffer' language.
-   */
   public static String protobufLanguageName() {
     return LANGUAGE_NAME;
   }
 
+  public static String protobufPluginId() {
+    return PLUGIN_ID;
+  }
+
   private ProtobufEditorPlugIn() {}
 }
diff --git a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/preferences/paths/DirectorySelectionDialogs.java b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/preferences/paths/DirectorySelectionDialogs.java
index 8d993e0..42f9f7f 100644
--- a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/preferences/paths/DirectorySelectionDialogs.java
+++ b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/preferences/paths/DirectorySelectionDialogs.java
@@ -8,10 +8,9 @@
  */
 package com.google.eclipse.protobuf.ui.preferences.paths;
 
-import static com.google.eclipse.protobuf.ui.ProtobufUiModule.PLUGIN_ID;
 import static com.google.eclipse.protobuf.ui.preferences.paths.Messages.*;
+import static com.google.eclipse.protobuf.ui.util.IStatusFactory.error;
 import static com.google.eclipse.protobuf.ui.util.Workspaces.workspaceRoot;
-import static org.eclipse.core.runtime.IStatus.ERROR;
 import static org.eclipse.core.runtime.Status.OK_STATUS;
 import static org.eclipse.jface.window.Window.OK;
 import static org.eclipse.ui.views.navigator.ResourceComparator.NAME;
@@ -55,7 +54,7 @@
     dialog.setValidator(new ISelectionStatusValidator() {
       @Override public IStatus validate(Object[] selection) {
         if (selection != null && selection.length > 0 && selection[0] instanceof IFile) {
-          return new Status(ERROR, PLUGIN_ID, errorElementIsNotDirectory);
+          return error(errorElementIsNotDirectory);
         }
         return OK_STATUS;
       }
diff --git a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/preferences/paths/PathsPreferencePage.java b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/preferences/paths/PathsPreferencePage.java
index ea1c09d..00740ec 100644
--- a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/preferences/paths/PathsPreferencePage.java
+++ b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/preferences/paths/PathsPreferencePage.java
@@ -9,11 +9,11 @@
 package com.google.eclipse.protobuf.ui.preferences.paths;
 
 import static com.google.common.collect.Lists.newArrayList;
-import static com.google.eclipse.protobuf.ui.ProtobufUiModule.PLUGIN_ID;
 import static com.google.eclipse.protobuf.ui.preferences.pages.ButtonGroup.with;
 import static com.google.eclipse.protobuf.ui.preferences.pages.binding.BindingToButtonSelection.bindSelectionOf;
 import static com.google.eclipse.protobuf.ui.preferences.paths.Messages.*;
 import static com.google.eclipse.protobuf.ui.preferences.paths.PreferenceNames.*;
+import static com.google.eclipse.protobuf.ui.util.IStatusFactory.error;
 import static java.util.Collections.unmodifiableList;
 import static org.eclipse.core.resources.IncrementalProjectBuilder.FULL_BUILD;
 import static org.eclipse.core.runtime.Status.OK_STATUS;
@@ -22,7 +22,6 @@
 
 import java.util.List;
 
-import org.apache.log4j.Logger;
 import org.eclipse.core.runtime.*;
 import org.eclipse.core.runtime.jobs.Job;
 import org.eclipse.swt.SWT;
@@ -42,8 +41,6 @@
  * @author alruiz@google.com (Alex Ruiz)
  */
 public class PathsPreferencePage extends PreferenceAndPropertyPage {
-  private static Logger logger = Logger.getLogger(PathsPreferencePage.class);
-
   private static final String COMMA_DELIMITER = ",";
   private static final String PREFERENCE_PAGE_ID = PathsPreferencePage.class.getName();
 
@@ -198,8 +195,7 @@
         try {
           project().build(FULL_BUILD, monitor);
         } catch (CoreException e) {
-          logger.error(e.getMessage(), e);
-          return new Status(ERROR, PLUGIN_ID, ERROR, e.getMessage(), e);
+          return error(e);
         }
         return OK_STATUS;
       }
diff --git a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/util/IStatusFactory.java b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/util/IStatusFactory.java
new file mode 100644
index 0000000..89d5571
--- /dev/null
+++ b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/util/IStatusFactory.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2012 Google Inc.
+ *
+ * All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License v1.0 which accompanies this distribution, and is available at
+ *
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package com.google.eclipse.protobuf.ui.util;
+
+import static com.google.common.base.Strings.nullToEmpty;
+import static com.google.eclipse.protobuf.ui.plugin.ProtobufEditorPlugIn.protobufPluginId;
+import static org.eclipse.core.runtime.IStatus.ERROR;
+
+import org.eclipse.core.runtime.*;
+
+/**
+ * Factory of <code>{@link IStatus}</code>.
+ *
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+public class IStatusFactory {
+  public static IStatus error(Throwable cause) {
+    String message = nullToEmpty(cause.getMessage());
+    return error(message, cause);
+  }
+
+  public static IStatus error(String message, Throwable cause) {
+    return new Status(ERROR, protobufPluginId(), message, cause);
+  }
+
+  public static IStatus error(String message) {
+    return new Status(ERROR, protobufPluginId(), message);
+  }
+
+  private IStatusFactory() {}
+}
diff --git a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/util/editor/ChangedLineRegionCalculator.java b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/util/editor/ChangedLineRegionCalculator.java
index 6057149..7566418 100644
--- a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/util/editor/ChangedLineRegionCalculator.java
+++ b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/util/editor/ChangedLineRegionCalculator.java
@@ -1,20 +1,18 @@
 /*
  * Copyright (c) 2011 Google Inc.
  *
- * All rights reserved. This program and the accompanying materials are made
- * available under the terms of the Eclipse Public License v1.0 which
- * accompanies this distribution, and is available at
+ * All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License v1.0 which accompanies this distribution, and is available at
  *
  * http://www.eclipse.org/legal/epl-v10.html
  */
 package com.google.eclipse.protobuf.ui.util.editor;
 
 import static com.google.common.collect.Lists.newArrayList;
-import static com.google.eclipse.protobuf.ui.ProtobufUiModule.PLUGIN_ID;
+import static com.google.eclipse.protobuf.ui.util.IStatusFactory.error;
 import static com.google.eclipse.protobuf.ui.util.editor.Messages.*;
 import static org.eclipse.compare.rangedifferencer.RangeDifferencer.findDifferences;
 import static org.eclipse.core.filebuffers.FileBuffers.createTextFileBufferManager;
-import static org.eclipse.core.runtime.IStatus.ERROR;
 import static org.eclipse.core.runtime.Status.OK_STATUS;
 import static org.eclipse.core.runtime.SubProgressMonitor.PREPEND_MAIN_LABEL_TO_SUBTASK;
 
@@ -47,7 +45,7 @@
       SafeRunner.run(new ISafeRunnable() {
         @Override public void handleException(Throwable exception) {
           logger.error(exception.getMessage(), exception);
-          errorStatus.set(new Status(ERROR, PLUGIN_ID, 0, errorCalculatingChangedRegions, exception));
+          errorStatus.set(error(errorCalculatingChangedRegions, exception));
           result.set(null);
         }
 
diff --git a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/wizards/NewProtoFileWizard.java b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/wizards/NewProtoFileWizard.java
new file mode 100644
index 0000000..59c4936
--- /dev/null
+++ b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/wizards/NewProtoFileWizard.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2011 Google Inc.
+ *
+ * All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License v1.0 which accompanies this distribution, and is available at
+ *
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package com.google.eclipse.protobuf.ui.wizards;
+
+import static com.google.common.io.Closeables.closeQuietly;
+import static com.google.eclipse.protobuf.ui.util.IStatusFactory.error;
+import static com.google.eclipse.protobuf.ui.util.Workbenches.activeWorkbenchPage;
+import static com.google.eclipse.protobuf.ui.util.Workspaces.workspaceRoot;
+import static org.eclipse.jface.dialogs.MessageDialog.openError;
+import static org.eclipse.ui.ide.IDE.openEditor;
+
+import java.io.*;
+import java.lang.reflect.InvocationTargetException;
+
+import org.eclipse.core.resources.*;
+import org.eclipse.core.runtime.*;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.jface.viewers.*;
+import org.eclipse.jface.wizard.Wizard;
+import org.eclipse.ui.*;
+
+/**
+ * Wizard for creation of new .proto files.
+ *
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+public class NewProtoFileWizard extends Wizard implements INewWizard {
+  private NewProtoFileWizardPage page;
+  private ISelection selection;
+
+  public NewProtoFileWizard() {
+    setNeedsProgressMonitor(true);
+  }
+
+  @Override public void addPages() {
+    page = new NewProtoFileWizardPage(selection);
+    addPage(page);
+  }
+
+  @Override public boolean performFinish() {
+    try {
+      getContainer().run(true, false, new IRunnableWithProgress() {
+        @Override public void run(IProgressMonitor monitor) throws InvocationTargetException {
+          try {
+            doFinish(page.containerName(), page.fileName(), monitor);
+          } catch (CoreException e) {
+            throw new InvocationTargetException(e);
+          } finally {
+            monitor.done();
+          }
+        }
+      });
+    } catch (InterruptedException e) {
+      return false;
+    } catch (InvocationTargetException e) {
+      Throwable realException = e.getTargetException();
+      openError(getShell(), "Error", realException.getMessage());
+      return false;
+    }
+    return true;
+  }
+
+  private void doFinish(String containerName, String fileName, IProgressMonitor monitor) throws CoreException {
+    monitor.beginTask("Creating " + fileName, 2);
+    IResource resource = workspaceRoot().findMember(new Path(containerName));
+    if (!resource.exists() || !(resource instanceof IContainer)) {
+      throw new CoreException(error("Container \"" + containerName + "\" does not exist."));
+    }
+    IContainer container = (IContainer) resource;
+    final IFile file = container.getFile(new Path(fileName));
+    InputStream stream = null;
+    try {
+      stream = openContentStream();
+      if (file.exists()) {
+        file.setContents(stream, true, true, monitor);
+      } else {
+        file.create(stream, true, monitor);
+      }
+    } finally {
+      closeQuietly(stream);
+    }
+    monitor.worked(1);
+    monitor.setTaskName("Opening file for editing...");
+    getShell().getDisplay().asyncExec(new Runnable() {
+      @Override public void run() {
+        IWorkbenchPage activePage = activeWorkbenchPage();
+        try {
+          openEditor(activePage, file, true);
+        } catch (PartInitException e) {
+        }
+      }
+    });
+    monitor.worked(1);
+  }
+
+  private InputStream openContentStream() {
+    String contents = "syntax = \"proto2\";";
+    return new ByteArrayInputStream(contents.getBytes());
+  }
+
+  @Override public void init(IWorkbench workbench, IStructuredSelection newSelection) {
+    selection = newSelection;
+  }
+}
\ No newline at end of file
diff --git a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/wizards/NewProtoFileWizardPage.java b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/wizards/NewProtoFileWizardPage.java
new file mode 100644
index 0000000..e7f4588
--- /dev/null
+++ b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/wizards/NewProtoFileWizardPage.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (c) 2011 Google Inc.
+ *
+ * All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License v1.0 which accompanies this distribution, and is available at
+ *
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package com.google.eclipse.protobuf.ui.wizards;
+
+import static com.google.eclipse.protobuf.ui.util.Workspaces.workspaceRoot;
+import static org.eclipse.jface.window.Window.OK;
+import static org.eclipse.swt.SWT.*;
+import static org.eclipse.swt.layout.GridData.FILL_HORIZONTAL;
+
+import org.eclipse.core.resources.*;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.jface.viewers.*;
+import org.eclipse.jface.wizard.WizardPage;
+import org.eclipse.swt.events.*;
+import org.eclipse.swt.layout.*;
+import org.eclipse.swt.widgets.*;
+import org.eclipse.ui.dialogs.ContainerSelectionDialog;
+
+/**
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+public class NewProtoFileWizardPage extends WizardPage {
+  private Text containerText;
+  private Text fileText;
+  private final ISelection selection;
+
+  public NewProtoFileWizardPage(ISelection selection) {
+    super("wizardPage");
+    setTitle("Multi-page Editor File");
+    setDescription("This wizard creates a new file with *.proto extension that can be opened by a multi-page editor.");
+    this.selection = selection;
+  }
+
+  @Override public void createControl(Composite parent) {
+    Composite container = new Composite(parent, NULL);
+    GridLayout layout = new GridLayout();
+    container.setLayout(layout);
+    layout.numColumns = 3;
+    layout.verticalSpacing = 9;
+    Label label = new Label(container, NULL);
+    label.setText("&Container:");
+
+    containerText = new Text(container, BORDER | SINGLE);
+    containerText.setLayoutData(new GridData(FILL_HORIZONTAL));
+    containerText.addModifyListener(new ModifyListener() {
+      @Override public void modifyText(ModifyEvent e) {
+        dialogChanged();
+      }
+    });
+
+    Button button = new Button(container, PUSH);
+    button.setText("Browse...");
+    button.addSelectionListener(new SelectionAdapter() {
+      @Override public void widgetSelected(SelectionEvent e) {
+        handleBrowse();
+      }
+    });
+    label = new Label(container, NULL);
+    label.setText("&File name:");
+
+    fileText = new Text(container, BORDER | SINGLE);
+    fileText.setLayoutData(new GridData(FILL_HORIZONTAL));
+    fileText.addModifyListener(new ModifyListener() {
+      @Override public void modifyText(ModifyEvent e) {
+        dialogChanged();
+      }
+    });
+    initialize();
+    dialogChanged();
+    setControl(container);
+  }
+
+  private void initialize() {
+    Object selected = firstSelectedElement();
+    if (selected != null) {
+      if (selected instanceof IResource) {
+        IContainer container;
+        if (selected instanceof IContainer) {
+          container = (IContainer) selected;
+        } else {
+          container = ((IResource) selected).getParent();
+        }
+        containerText.setText(container.getFullPath().toString());
+      }
+    }
+    fileText.setText("new_file.proto");
+  }
+
+  private Object firstSelectedElement() {
+    if (selection == null || selection.isEmpty()) {
+      return null;
+    }
+    if (selection instanceof IStructuredSelection) {
+      ((IStructuredSelection) selection).getFirstElement();
+    }
+    return null;
+  }
+
+  private void handleBrowse() {
+    ContainerSelectionDialog dialog = new ContainerSelectionDialog(getShell(), workspaceRoot(), false,
+        "Select new file container");
+    if (dialog.open() == OK) {
+      Object[] result = dialog.getResult();
+      if (result.length == 1) {
+        containerText.setText(((Path) result[0]).toString());
+      }
+    }
+  }
+
+  private void dialogChanged() {
+    IResource container = ResourcesPlugin.getWorkspace().getRoot().findMember(new Path(containerName()));
+    String fileName = fileName();
+    if (containerName().isEmpty()) {
+      updateStatus("File container must be specified");
+      return;
+    }
+    if (container == null || (container.getType() & (IResource.PROJECT | IResource.FOLDER)) == 0) {
+      updateStatus("File container must exist");
+      return;
+    }
+    if (!container.isAccessible()) {
+      updateStatus("Project must be writable");
+      return;
+    }
+    if (fileName.isEmpty()) {
+      updateStatus("File name must be specified");
+      return;
+    }
+    if (fileName.replace('\\', '/').indexOf('/', 1) > 0) {
+      updateStatus("File name must be valid");
+      return;
+    }
+    int dotLoc = fileName.lastIndexOf('.');
+    if (dotLoc != -1) {
+      String ext = fileName.substring(dotLoc + 1);
+      if (ext.equalsIgnoreCase("proto") == false) {
+        updateStatus("File extension must be \"proto\"");
+        return;
+      }
+    }
+    updateStatus(null);
+  }
+
+  private void updateStatus(String message) {
+    setErrorMessage(message);
+    setPageComplete(message == null);
+  }
+
+  public String containerName() {
+    return containerText.getText();
+  }
+
+  public String fileName() {
+    return fileText.getText();
+  }
+}
\ No newline at end of file