n progress: [Issue 104] Update "Next Id" comment when generating
a the tag number of a field or literal

Added dialog where users can add or edit patterns.
diff --git a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/preferences/pages/compiler/EditCodeGenerationDialog.java b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/preferences/pages/compiler/EditCodeGenerationDialog.java
index 1271273..26c3b60 100644
--- a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/preferences/pages/compiler/EditCodeGenerationDialog.java
+++ b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/preferences/pages/compiler/EditCodeGenerationDialog.java
@@ -68,9 +68,9 @@
     txtOutputDirectory.setText(option.outputDirectory());
 
     txtError = new Text(cmpDialogArea, SWT.READ_ONLY | SWT.WRAP);
-    GridData gd_lblError = new GridData(GRAB_HORIZONTAL | HORIZONTAL_ALIGN_FILL);
-    gd_lblError.horizontalSpan = 2;
-    txtError.setLayoutData(gd_lblError);
+    GridData gridData = new GridData(GRAB_HORIZONTAL | HORIZONTAL_ALIGN_FILL);
+    gridData.horizontalSpan = 2;
+    txtError.setLayoutData(gridData);
     txtError.setBackground(txtError.getDisplay().getSystemColor(SWT.COLOR_WIDGET_BACKGROUND));
 
     addEventListeners();
diff --git a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/preferences/pages/editor/numerictag/AddOrEditPatternDialog.java b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/preferences/pages/editor/numerictag/AddOrEditPatternDialog.java
new file mode 100644
index 0000000..22293b5
--- /dev/null
+++ b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/preferences/pages/editor/numerictag/AddOrEditPatternDialog.java
@@ -0,0 +1,161 @@
+/*
+ * 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.preferences.pages.editor.numerictag;
+
+import static java.util.regex.Pattern.CASE_INSENSITIVE;
+import static org.eclipse.jface.dialogs.IDialogConstants.OK_ID;
+import static org.eclipse.swt.layout.GridData.*;
+import static org.eclipse.xtext.util.Strings.isEmpty;
+
+import com.google.eclipse.protobuf.ui.preferences.InputDialog;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.*;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.*;
+
+import java.util.regex.*;
+
+/**
+ * Dialog where users can enter a new pattern or edit an existing one.
+ * 
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+public class AddOrEditPatternDialog extends InputDialog {
+  
+  private Text txtPattern;
+  private Text txtTest;
+  private Text txtPatternError;
+  private Text txtTestError;
+
+  private String pattern;
+  
+  public static AddOrEditPatternDialog editPattern(String pattern, Shell parent) {
+    AddOrEditPatternDialog dialog = new AddOrEditPatternDialog(parent, "Edit Pattern");
+    dialog.pattern = pattern;
+    return dialog;
+  }
+  
+  public static AddOrEditPatternDialog addPattern(Shell parent) {
+    return new AddOrEditPatternDialog(parent, "Add New Pattern");
+  }
+  
+  public AddOrEditPatternDialog(Shell parent, String title) {
+    super(parent, title);
+  }
+  
+  /** {@inheritDoc} */
+  @Override protected Control createDialogArea(Composite parent) {
+    Composite cmpDialogArea = (Composite) super.createDialogArea(parent);
+    
+    Label lblPattern = new Label(cmpDialogArea, SWT.NONE);
+    lblPattern.setText("Pattern:");
+    
+    txtPattern = new Text(cmpDialogArea, SWT.BORDER);
+    txtPattern.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
+    if (!isEmpty(pattern)) {
+      txtPattern.setText(pattern);
+      txtPattern.selectAll();
+    }
+    
+    txtPatternError = new Text(cmpDialogArea, SWT.READ_ONLY | SWT.WRAP);
+    txtPatternError.setLayoutData(new GridData(GRAB_HORIZONTAL | HORIZONTAL_ALIGN_FILL));
+    Color readOnlyColor = txtPatternError.getDisplay().getSystemColor(SWT.COLOR_WIDGET_BACKGROUND);
+    txtPatternError.setBackground(readOnlyColor);
+    
+    Label lblSeparator = new Label(cmpDialogArea, SWT.SEPARATOR | SWT.HORIZONTAL);
+    lblSeparator.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
+    
+    Label lblTest = new Label(cmpDialogArea, SWT.NONE);
+    lblTest.setText("Test: (optional)");
+    
+    txtTest = new Text(cmpDialogArea, SWT.BORDER);
+    txtTest.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
+    
+    txtTestError = new Text(cmpDialogArea, SWT.READ_ONLY | SWT.WRAP);
+    txtTestError.setLayoutData(new GridData(GRAB_HORIZONTAL | HORIZONTAL_ALIGN_FILL));
+    txtTestError.setBackground(readOnlyColor);
+    
+    addEventListeners();
+    
+    applyDialogFont(cmpDialogArea);
+    return cmpDialogArea;
+  }
+
+  private void addEventListeners() {
+    txtPattern.addModifyListener(new ModifyListener() {
+      public void modifyText(ModifyEvent e) {
+        String regex = enteredPattern();
+        if (isEmpty(regex)) {
+          clearTestErrorText();
+          okButton().setEnabled(false);
+          return;
+        }
+        try {
+          Pattern.compile(regex, CASE_INSENSITIVE);
+        } catch (PatternSyntaxException error) {
+          txtPatternError.setText(error.getMessage());
+          clearTestErrorText();
+          okButton().setEnabled(false);
+          return;
+        }
+        testPattern();
+        okButton().setEnabled(true);
+      }
+    });
+    txtTest.addModifyListener(new ModifyListener() {
+      public void modifyText(ModifyEvent e) {
+        testPattern();
+      }
+    });
+  }
+
+  private void testPattern() {
+    String regex = enteredPattern();
+    String testText = txtTest.getText().trim();
+    if (isEmpty(regex) || isEmpty(testText)) {
+      clearTestErrorText();
+      return;
+    }
+    Pattern p = Pattern.compile(regex, CASE_INSENSITIVE);
+    Matcher matcher = p.matcher(testText);
+    String result = matcher.matches() ? "Match!" : "Does not match!";
+    txtTestError.setText(result);
+  }
+
+  private String enteredPattern() {
+    return txtPattern.getText().trim();
+  }
+  
+  public String pattern() {
+    return pattern;
+  }
+  
+  /** {@inheritDoc} */
+  @Override protected void createButtonsForButtonBar(Composite parent) {
+    super.createButtonsForButtonBar(parent);
+    okButton().setEnabled(!isEmpty(pattern));
+    txtPattern.setFocus();
+  }
+
+  private Button okButton() {
+    return getButton(OK_ID);
+  }
+
+  private void clearTestErrorText() {
+    txtTestError.setText("");
+  }
+
+  /** {@inheritDoc} */
+  @Override protected void okPressed() {
+    pattern = enteredPattern();
+    super.okPressed();
+  }
+}
diff --git a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/preferences/pages/editor/numerictag/NumericTagPreferencePage.java b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/preferences/pages/editor/numerictag/NumericTagPreferencePage.java
index 4b5f513..9e5319d 100644
--- a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/preferences/pages/editor/numerictag/NumericTagPreferencePage.java
+++ b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/preferences/pages/editor/numerictag/NumericTagPreferencePage.java
@@ -9,7 +9,13 @@
 package com.google.eclipse.protobuf.ui.preferences.pages.editor.numerictag;
 
 import static com.google.eclipse.protobuf.ui.preferences.binding.BindingToListItems.bindItemsOf;
+import static com.google.eclipse.protobuf.ui.preferences.pages.editor.numerictag.AddOrEditPatternDialog.*;
 import static com.google.eclipse.protobuf.ui.preferences.pages.editor.numerictag.Messages.*;
+import static org.eclipse.jface.window.Window.OK;
+
+import com.google.eclipse.protobuf.ui.preferences.*;
+import com.google.eclipse.protobuf.ui.preferences.binding.PreferenceBinder;
+import com.google.eclipse.protobuf.ui.preferences.pages.PreferenceAndPropertyPage;
 
 import org.eclipse.jface.preference.IPreferenceStore;
 import org.eclipse.jface.viewers.ListViewer;
@@ -18,10 +24,6 @@
 import org.eclipse.swt.layout.*;
 import org.eclipse.swt.widgets.*;
 
-import com.google.eclipse.protobuf.ui.preferences.*;
-import com.google.eclipse.protobuf.ui.preferences.binding.PreferenceBinder;
-import com.google.eclipse.protobuf.ui.preferences.pages.PreferenceAndPropertyPage;
-
 /**
  * Preference page where users can specify the patterns to use to match comments where "the next id" is being tracked.
  *
@@ -77,6 +79,23 @@
         enableButtonsDependingOnListSelection();
       }
     });
+    btnAdd.addSelectionListener(new SelectionAdapter() {
+      @Override public void widgetSelected(SelectionEvent e) {
+        AddOrEditPatternDialog dialog = addPattern(getShell());
+        if (dialog.open() == OK) {
+          lstPaths.add(dialog.pattern());
+        }
+      }
+    });
+    btnEdit.addSelectionListener(new SelectionAdapter() {
+      @Override public void widgetSelected(SelectionEvent e) {
+        int selectionIndex = lstPaths.getSelectionIndex();
+        AddOrEditPatternDialog dialog = editPattern(lstPaths.getItem(selectionIndex), getShell());
+        if (dialog.open() == OK) {
+          lstPaths.setItem(selectionIndex, dialog.pattern());
+        }
+      }
+    });
     btnRemove.addSelectionListener(new SelectionAdapter() {
       @Override public void widgetSelected(SelectionEvent e) {
         int index = lstPaths.getSelectionIndex();