Index: trunk/Changes
===================================================================
--- trunk/Changes	(revision 23)
+++ trunk/Changes	(revision 24)
@@ -9,4 +9,5 @@
     * allow option lists to have simple multiword and quoted
       string values
+    * allow for validated but not required fields
     
 0.05 -  9 Nov 2004
Index: trunk/lib/Text/FormBuilder.pm
===================================================================
--- trunk/lib/Text/FormBuilder.pm	(revision 23)
+++ trunk/lib/Text/FormBuilder.pm	(revision 24)
@@ -83,9 +83,4 @@
             }
         }
-    }
-    
-    # remove extraneous undefined values
-    for my $field (@{ $self->{form_spec}{fields} }) {
-        defined $$field{$_} or delete $$field{$_} foreach keys %{ $field };
     }
     
@@ -145,9 +140,18 @@
     }
     
-
+    # remove extraneous undefined values
+    for my $field (@{ $self->{form_spec}{fields} }) {
+        defined $$field{$_} or delete $$field{$_} foreach keys %{ $field };
+    }
+    
+    # because this messes up things at the CGI::FormBuilder::field level
+    # it seems to be marking required based on the existance of a 'required'
+    # param, not whether it is true or defined
+    $$_{required} or delete $$_{required} foreach @{ $self->{form_spec}{fields} };
 
     
     $self->{form} = CGI::FormBuilder->new(
         %DEFAULT_OPTIONS,
+        required => [ map { $$_{name} } grep { $$_{required} } @{ $self->{form_spec}{fields} } ],
         title => $self->{form_spec}{title},
         template => {
@@ -218,4 +222,5 @@
         %DEFAULT_OPTIONS,
         title => $self->{form_spec}{title},
+        required => [ map { $$_{name} } grep { $$_{required} } @{ $self->{form_spec}{fields} } ],
         template => {
             type => 'Text',
@@ -327,9 +332,7 @@
         $OUT .= (grep { $$_{invalid} } @group_fields) ? qq[  <tr class="invalid">\n] : qq[  <tr>\n];
         
-        
         #TODO: validated but not required fields
         # in a form spec: //EMAIL?
         
-        #TODO: this doesn't seem to be working; all groups are getting marked as required        
         $OUT .= '    <th class="label">';
         $OUT .= (grep { $$_{required} } @group_fields) ? qq[<strong class="required">$$line[1]{label}:</strong>] : "$$line[1]{label}:";
@@ -362,4 +365,5 @@
     td ul { list-style: none; padding-left: 0; margin-left: 0; }
     .sublabel { color: #999; }
+    .invalid { background: red; }
   </style>
 </head>
@@ -586,6 +590,23 @@
 =head2 Fields
 
-Form fields are each described on a single line. The simplest field is just a
-name:
+First, a note about multiword strings in the fields. Anywhere where it says
+that you may use a multiword string, this means that you can do one of two
+things. For strings that consist solely of alphanumeric characters (i.e.
+C<\w+>) and spaces, the string will be recognized as is:
+
+    field_1|A longer label
+
+If you want to include non-alphanumerics (e.g. punctuation), you must 
+single-quote the string:
+
+    field_2|'Dept./Org.'
+
+To include a literal single-quote in a single-quoted string, escape it with
+a backslash:
+
+    field_3|'\'Official\' title'
+
+Now, back to the basics. Form fields are each described on a single line.
+The simplest field is just a name (which cannot contain any whitespace):
 
     color
@@ -597,5 +618,5 @@
     color|Favorite color
 
-Field names cannot contain whitespace, but the descriptive label can.
+The descriptive label can be a multiword string, as described above.
 
 To use a different input type:
@@ -629,7 +650,6 @@
     color|Favorite color:select{red,blue,green}
 
-Values are in a comma-separated list inside curly braces. Whitespace
-between values is irrelevant, although there cannot be any whitespace
-within a value.
+Values are in a comma-separated list of single words or multiword strings
+inside curly braces. Whitespace between values is irrelevant.
 
 To add more descriptive display text to a vlaue in a list, add a square-bracketed
@@ -637,6 +657,4 @@
 
     ...:select{red[Scarlet],blue[Azure],green[Olive Drab]}
-
-As you can see, spaces I<are> allowed within the display text for a value.
 
 If you have a list of options that is too long to fit comfortably on one line,
@@ -661,8 +679,8 @@
 { value2 => 'Description 2}, ... ) >> form.
 
-B<NOTE:> This feature of the language may go away unless I find a compelling
+I<B<NOTE:> This feature of the language may go away unless I find a compelling
 reason for it in the next few versions. What I really wanted was lists that
 were filled in at run-time (e.g. from a database), and that can be done easily
-enough with the CGI::FormBuilder object directly.
+enough with the CGI::FormBuilder object directly.>
 
 You can also supply a default value to the field. To get a default value of
@@ -670,4 +688,6 @@
 
     color|Favorite color:select=green{red,blue,green}
+
+Default values can also be either single words or multiword strings.
 
 To validate a field, include a validation type at the end of the field line:
@@ -687,4 +707,12 @@
     title//VALUE
 
+By default, adding a validation type to a field makes that field required. To
+change this, add a C<?> to the end of the validation type:
+
+    contact//EMAIL?
+
+In this case, you would get a C<contact> field that was optional, but if it
+were filled in, would have to validate as an C<EMAIL>.
+
 =head2 Comments
 
@@ -702,6 +730,4 @@
 with their own id and (optional) heading
 
-Optional validated fields; marked like C<//EMAIL?>
-
 Better examples in the docs (maybe a standalone or two as well)
 
Index: trunk/lib/Text/FormBuilder/grammar
===================================================================
--- trunk/lib/Text/FormBuilder/grammar	(revision 23)
+++ trunk/lib/Text/FormBuilder/grammar	(revision 24)
@@ -14,4 +14,5 @@
 	$type,
 	@options,
+	$required,
 	$list_var,
 	$size,
@@ -105,4 +106,5 @@
             list     => $list_var,
             validate => $item{'validate(?)'}[0],
+	    required => $required || 0,
 	};
 	
@@ -121,5 +123,8 @@
 	}
 	
+	#warn "field $item{name} is required" if $required;
+	
 	$type = undef;
+	$required = 0;
 	$list_var = undef;
 	$size = undef;
@@ -164,5 +169,5 @@
 list_var: /@[A-Z_]+/ { $list_var = $item[1] }
 
-option: (simple_multiword | quoted_string) display_text(?)
+option: (value | simple_multiword | quoted_string) display_text(?)
     { push @options, { $item[1] => $item{'display_text(?)'}[0] } }
 
@@ -171,5 +176,9 @@
 display_text: '[' /[^\]]+/i ']'    { $item[2] }
 
-validate: '//' value
+validate: '//' (optional_pattern | required_pattern)    { $item[2] }
+
+optional_pattern: /[A-Z_]+/ '?'	{ $required = 0; $item[1] }
+
+required_pattern: /[A-Z_]+/ { $required = 1; $item[1] }
 
 comment: '#' /.*/
