--- loncom/homework/optionresponse.pm	2010/08/23 11:33:29	1.169
+++ loncom/homework/optionresponse.pm	2011/09/30 16:47:08	1.180
@@ -1,7 +1,7 @@
 # LearningOnline Network with CAPA
 # option list style responses
 #
-# $Id: optionresponse.pm,v 1.169 2010/08/23 11:33:29 www Exp $
+# $Id: optionresponse.pm,v 1.180 2011/09/30 16:47:08 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -103,7 +103,7 @@ sub start_foilgroup {
   my $result='';
   %Apache::response::foilgroup=();
   $Apache::optionresponse::conceptgroup=0;
-  &Apache::response::pushrandomnumber();
+  &Apache::response::pushrandomnumber(undef,$target);
   if ($target eq 'edit') {
     my $optionlist="<option></option>\n";
     my $option;
@@ -137,7 +137,10 @@ ENDTABLE
        }
        $result.='>'.$option.'</option>';
     }
-    $result.='</select></label>';  
+    $result.='</select></label>';
+    $result.=&Apache::edit::checked_arg('Checkbox options:','checkboxoptions',
+                                         [ ['nochoice','Don\'t show option list'] ]
+                                         ,$token).'</td>';
     $result.= &Apache::edit::end_row();
     $result.= &Apache::edit::start_spanning_row();
     $result.= $insertlist.'<br />';
@@ -152,17 +155,13 @@ ENDTABLE
       splice(@options,$delopt-1,1);
       $optchanged=1;
     }
-    if ($env{"form.$Apache::lonxml::curdepth.options"}) {
+    if ($env{"form.$Apache::lonxml::curdepth.options"} ne '') {
       my $newopt = $env{"form.$Apache::lonxml::curdepth.options"};
-      if ($options[0]) {
-	push(@options,$newopt);
-      } else {
-	$options[0]=$newopt;
-      }
+      push(@options,$newopt);
       $optchanged=1;
     }
     my $rebuildtag = &Apache::edit::get_new_args($token,$parstack,$safeeval,
-						 'texoptions','checkboxvalue');
+						 'texoptions','checkboxvalue','checkboxoptions');
     if ($optchanged || $rebuildtag ) {
       $result = "<foilgroup options=\"(";
       foreach my $option (@options) {
@@ -173,6 +172,7 @@ ENDTABLE
       chop $result;
       $result.=')" ';
       $result .= 'texoptions="'.$token->[2]{'texoptions'}.'" ';
+      $result .= 'checkboxoptions="'.$token->[2]{'checkboxoptions'}.'" ';
       $result .= 'checkboxvalue="'.$token->[2]{'checkboxvalue'}.'"';
       $result .= '>';
     } # else nothing changed so just use the default mechanism
@@ -202,13 +202,14 @@ sub end_foilgroup {
     my $TeXlayout=&Apache::lonxml::get_param('TeXlayout',$parstack,$safeeval,
 					     -2,0);
     my $checkboxvalue=&Apache::lonxml::get_param('checkboxvalue',$parstack,$safeeval);
+    my $checkboxchoices=(&Apache::lonxml::get_param('checkboxoptions',$parstack,$safeeval) ne 'nochoice');
     if ($target eq 'tex' && $tex_option_switch eq 'nochoice') {@opt=();}
     &Apache::lonxml::debug("Options are $#opt");
     my $max = &Apache::lonxml::get_param('max',$parstack,$safeeval,'-2');
     my $randomize = &Apache::lonxml::get_param('randomize',$parstack,
 					       $safeeval,'-2');
     if ($target eq 'web' || $target eq 'tex') {
-      $result.=&displayfoils($target,$max,$randomize,$TeXlayout,$checkboxvalue,@opt);
+      $result.=&displayfoils($target,$max,$randomize,$TeXlayout,$checkboxvalue,$checkboxchoices,@opt);
       $Apache::lonxml::post_evaluate=0;
     } elsif ( $target eq 'answer') {
       $result.=&displayanswers($max,$randomize,@opt);
@@ -220,7 +221,26 @@ sub end_foilgroup {
 	my $part_id="$Apache::inputtags::part.$Apache::inputtags::response[-1]";
 	push (@{ $Apache::lonhomework::analyze{"$part_id.options"} },@opt);
     } elsif ( $target eq 'grade') {
+
+      my $nonlenient=0;
+      my $part=$Apache::inputtags::part;
+      my $lenientparm=&Apache::lonnet::EXT("resource.$part.lenient");
+
+      if ($Apache::lonhomework::scantronmode) {
+         $nonlenient=0;
+# Grading an exam: we are grading lenient unless told not to
+         if ($lenientparm=~/^0|off|no$/i) {
+            $nonlenient=1;
+         }
+      } else {
+# Web mode: we are non-lenient unless told to
+         $nonlenient=1;
+         if ($lenientparm=~/^1|on|yes$/i) {
+            $nonlenient=0;
+         }
+      }
       if ( &Apache::response::submitted()) {
+
 	my @whichopt = &whichfoils($max,$randomize);
 	my $temp=1;my $name;
 	my %responsehash;
@@ -228,11 +248,43 @@ sub end_foilgroup {
 	my $right=0;
 	my $wrong=0;
 	my $ignored=0;
+        my ($numrows,$bubbles_per_row);
+        if ($Apache::lonhomework::scantronmode) {
+            my $numitems = scalar(@opt);
+            ($numrows,$bubbles_per_row) =
+                &Apache::optionresponse::getnumrows($numitems);
+        }
+        if ($numrows < 1) {
+            $numrows = 1;
+        }
 	foreach $name (@whichopt) {
-	  my $response=&Apache::response::getresponse($temp);
-	  if ($env{'form.submitted'} eq 'scantron' && $response=~/\S/) {
-	      $response = $opt[$response];
-	  }
+	  my $response;
+          if ($env{'form.submitted'} eq 'scantron') {
+              if ($numrows > 1) {
+                  my $num = $temp;
+                  my $totalnum;
+                  for (my $i=0; $i<$numrows; $i++) {
+                      my $item = &Apache::response::getresponse($num);
+                      if ($item =~ /^\d+$/) {
+                          $totalnum = $i*$bubbles_per_row + $item;
+                      }
+                      $num ++;
+                  }
+                  if ($totalnum =~ /^\d+$/) {
+                      $response = $opt[$totalnum];
+                  }
+                  $temp += $numrows;
+              } else {
+                  $response=&Apache::response::getresponse($temp);
+                  if ($response=~/\S/) {
+                      $response = $opt[$response];
+                  }
+                  $temp ++;
+              }
+	  } else {
+              $response=&Apache::response::getresponse($temp);
+              $temp ++;
+          }
 	  if ( $response =~ /[^\s]/) {
 	    $responsehash{$name}=$response;
 	    my $value=$Apache::response::foilgroup{$name.'.value'};
@@ -245,7 +297,6 @@ sub end_foilgroup {
 	  } else {
 	    $ignored++;
 	  }
-	  $temp++;
 	}
 	my $part=$Apache::inputtags::part;
 	my $id = $Apache::inputtags::response['-1'];
@@ -257,7 +308,9 @@ sub end_foilgroup {
 	$Apache::lonhomework::results{"resource.$part.$id.submission"}=
 	    $responsestr;
 	$Apache::lonhomework::results{"resource.$part.$id.submissiongrading"}=$gradestr;
-	
+        if ($Apache::lonhomework::type eq 'randomizetry') {
+            $Apache::lonhomework::results{"resource.$part.$id.foilorder"} = &Apache::lonnet::array2str(@whichopt);
+        }
 	if (($Apache::lonhomework::type eq 'survey') ||
             ($Apache::lonhomework::type eq 'surveycred') ||
             ($Apache::lonhomework::type eq 'anonsurvey') ||
@@ -279,7 +332,10 @@ sub end_foilgroup {
 		my $ad=$Apache::lonhomework::results{"resource.$part.$id.awarddetail"}='MISSING_ANSWER';
 		&Apache::response::handle_previous(\%previous,$ad);
 	    }
-	} elsif (!$Apache::lonhomework::scantronmode) {
+	} elsif ($nonlenient) {
+#
+# Non-lenient mode. All right or all wrong
+#
 	    my $ad;
 	    if ($wrong==0 && $ignored==0) {
 		$ad='EXACT_ANS';
@@ -295,6 +351,9 @@ sub end_foilgroup {
 	    $Apache::lonhomework::results{"resource.$part.$id.awarddetail"}=$ad;
 	    &Apache::response::handle_previous(\%previous,$ad);
 	} else {
+#
+# This is lenient mode
+#
 	    my $ad;
 	    if ($wrong==0 && $right==0) {
 		#nothing submitted only assign a score if we 
@@ -315,8 +374,16 @@ sub end_foilgroup {
     }
     my $part_id     = $Apache::inputtags::part;
     my $response_id = $Apache::inputtags::response[-1];
-    &Apache::lonxml::increment_counter(&getfoilcounts($max),
-				       "$part_id.$response_id");
+    my ($numrows,$bubbles_per_row);
+    if (($target eq 'tex') && ($Apache::lonhomework::type eq 'exam')) {
+        ($numrows,$bubbles_per_row) =
+            &Apache::optionresponse::getnumrows(scalar(@opt));
+    }
+    if ($numrows < 1) {
+        $numrows = 1;
+    }
+    my $increment = &getfoilcounts($max) * $numrows;
+    &Apache::lonxml::increment_counter($increment,"$part_id.$response_id");
     if ($target eq 'analyze') {
 	&Apache::lonhomework::set_bubble_lines();
     }
@@ -414,7 +481,7 @@ sub check_for_invalid {
 }
 
 sub displayfoils {
-  my ($target,$max,$randomize,$TeXlayout,$checkboxvalue,@opt)=@_;
+  my ($target,$max,$randomize,$TeXlayout,$checkboxvalue,$checkboxchoices,@opt)=@_;
   if (!defined(@{ $Apache::response::foilgroup{'names'} })) {return;}
   my @names = @{ $Apache::response::foilgroup{'names'} };
   my @truelist;
@@ -428,7 +495,6 @@ sub displayfoils {
   my $part=$Apache::inputtags::part;
   my $id=$Apache::inputtags::response[-1];
   my $break;
-  my $solved=$Apache::lonhomework::history{"resource.$part.solved"};
   if ( ($target ne 'tex') &&
        &Apache::response::show_answer() ) {
     my $temp=1;
@@ -451,7 +517,9 @@ sub displayfoils {
 		    $text='<b>'.$value.':</b> '.$text;
 		}
 	    } else {
-		$text='&#149;'.$text;
+                if (@whichopt > 1) {
+                    $text='&#149;'.$text;
+                }
 	    }
 	    $result.=$text."\n";
 	}
@@ -462,11 +530,24 @@ sub displayfoils {
     }
   } else {
     my $temp=1;
-    my %lastresponse=&Apache::lonnet::str2hash($Apache::lonhomework::history{"resource.$part.$id.submission"});
+    my %lastresponse;
+    my $newvariation;
+    if ((($Apache::lonhomework::history{"resource.$part.type"} eq 'randomizetry') ||
+        ($Apache::lonhomework::type eq 'randomizetry')) &&
+        ($Apache::inputtags::status[-1] eq 'CAN_ANSWER')) {
+        if ($env{'form.'.$part.'.rndseed'} ne
+            $Apache::lonhomework::history{"resource.$part.rndseed"}) {
+            $newvariation = 1;
+        }
+    }
+    unless ($newvariation) {
+        %lastresponse=&Apache::lonnet::str2hash($Apache::lonhomework::history{"resource.$part.$id.submission"});
+    }
     my $internal_counter=$Apache::lonxml::counter;
     my $checkboxopt=&check_box_opt($target,$checkboxvalue,@opt);
     if ($checkboxopt) {
-       $result.='<br />'.&mt('Choices: ').'<b>'.$opt[0].','.$opt[1].'</b>. '.
+       $result.='<br />'.
+                ($checkboxchoices?&mt('Choices: ').'<b>'.$opt[0].','.$opt[1].'</b>. ':'').
                  &mt('Select all that are <b>[_1]</b>.',$checkboxopt);
     }
     foreach $name (@whichopt) {
@@ -542,7 +623,9 @@ sub displayfoils {
 	  }
 	  if ($text=~s|<drawoptionlist\s*/>|$optionlist|) {
 	      if ($Apache::lonhomework::type ne 'exam') {
-		  $text='&#149;'.$text;
+                  if (@whichopt > 1) {  
+                      $text='&#149;'.$text;
+                  }
 	      }
 	  } else {
 	      if ($Apache::lonhomework::type ne 'exam') {
@@ -583,9 +666,24 @@ sub displayfoils {
 	  }
 	  if ($Apache::lonhomework::type eq 'exam') {
 	      $result.='\vskip -1 mm\noindent';
-              $result.= '\textbf{'. $internal_counter.'}. \vskip -3mm'.&bubbles(\@alphabet,\@opt).
-		                 ' \strut ';
-	      $internal_counter++;
+              my ($numrows,$bubbles_per_row) = &getnumrows(scalar(@opt)); 
+              if ($numrows == 1) {  
+                  $result .= '\textbf{'.$internal_counter.'}. \vskip -3mm';
+              } else {
+                  my $linetext;
+                  for (my $i=0; $i<$numrows; $i++) {
+                      $linetext .= $internal_counter+$i.', ';
+                  }
+                  $linetext =~ s/,\s$//;
+                  $result .= '\small {\textbf{'.$linetext.'}} '.
+                             '\hskip 2 mm {\footnotesize '.
+                             &mt('(Bubble once in [_1] lines)',$numrows).
+                             '} \vskip 1 mm';
+              }
+              $result.= &bubbles(\@alphabet,\@opt,undef,undef,$numrows,
+                                 $bubbles_per_row,$internal_counter).
+                        ' \strut ';
+	      $internal_counter += $numrows;
 	  }
           if ($target eq 'tex' && $env{'form.pdfFormFields'} eq 'yes'
               && $Apache::inputtags::status[-1] eq 'CAN_ANSWER'
@@ -600,9 +698,12 @@ sub displayfoils {
   }
 
   if ($target eq 'web') {
-      &Apache::response::setup_prior_tries_hash(\&Apache::rankresponse::format_prior_answer,
-						[\@whichopt,
-						 'submissiongrading']);
+      my $data = [\@whichopt,'submissiongrading'];
+      my $questiontype;
+      if ($Apache::lonhomework::type eq 'randomizetry') {
+          $questiontype = $Apache::lonhomework::type,
+      }
+      &Apache::response::setup_prior_tries_hash(\&Apache::rankresponse::format_prior_answer,$data,$questiontype);
   }
 
   if ($target ne 'tex') {
@@ -663,7 +764,8 @@ sub webbubbles {
 
 
 sub bubbles {
-    my ($ralphabet,$ropt,$response, $max_width) = @_;
+    my ($ralphabet,$ropt,$response,$max_width,$numrows,$bubbles_per_row,
+        $internal_counter) = @_;
     my @alphabet = @$ralphabet;
     my @opt = @$ropt;
     my ($result,$head,$line) =('','','');
@@ -684,28 +786,37 @@ sub bubbles {
     }
     &Apache::lonxml::debug("Final maxwidth: $textwidth");
     for (my $ind=0;$ind<=$number_of_bubbles;$ind++) {
+        my $item;
+        if ($numrows > 1) {
+            my $num = $internal_counter+int($ind/$bubbles_per_row);
+            my $idx = int($ind % $bubbles_per_row);
+            $item = $num.$alphabet[$idx];
+        } else {
+            $item = $alphabet[$ind];
+        }
 	my $leftmargin;
 	$opt[$ind]=&Apache::lonxml::latex_special_symbols($opt[$ind]);
 	if ($response eq 'rankresponse') {$opt[$ind]='Rank '.$opt[$ind];}
 	if ($ind==0) {$leftmargin=6;} else {$leftmargin=10;}
 
-	$current_length += (length($opt[$ind])+length($alphabet[$ind])+4)*2;
+	$current_length += (length($opt[$ind])+length($item)+4)*2;
+        
 	if ($current_length<($textwidth-$leftmargin) and $ind!=$number_of_bubbles) {
-	    $line.='\hskip 1pc {\small \textbf{'.$alphabet[$ind].'}}$\bigcirc$\hskip -1 mm & \hskip -3 mm {\small '.$opt[$ind].'} & ';
+            
+	    $line.='\hskip 4 mm {\small \textbf{'.$item.'}}$\bigcirc$\hskip -1 mm & \hskip -3 mm {\small '.$opt[$ind].'} & ';
 	    $head.='lr';
 	} else {
 	    $line=~s/\&\s*$//;
-	    $result.='\vskip -2 mm\noindent\setlength{\tabcolsep}{2 mm}\begin{tabular}{'.$head.'}'.$line.'\\\\\end{tabular}\vskip 0 mm';
-	    $line = '\hskip -1 mm {\small \textbf{'.$alphabet[$ind].'}}$\bigcirc$\hskip -1 mm & \hskip -3 mm {\small '.$opt[$ind].'} & ';;
+	    $result.='\vskip -1 mm\noindent\setlength{\tabcolsep}{2 mm}\renewcommand{\arraystretch}{1.25}\begin{tabular}{'.$head.'}'.$line.'\\\\\end{tabular}\vskip 0 mm';
+	    $line = '\hskip 4 mm {\small \textbf{'.$item.'}}$\bigcirc$\hskip -1 mm & \hskip -3 mm {\small '.$opt[$ind].'} & ';
 	    $head ='lr';
-	    $current_length = (length($opt[$ind])+length($alphabet[$ind]))*2;
+	    $current_length = (length($opt[$ind])+length($item))*2;
 	}
 
     }
     return $result;
 }
 
-
 sub start_conceptgroup {
   my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
   $Apache::optionresponse::conceptgroup=1;
@@ -850,6 +961,24 @@ sub insert_foil {
 sub insert_drawoptionlist {
     return '<drawoptionlist />';
 }
+
+sub getnumrows {
+    my ($numitems) = @_;
+    my $bubbles_per_row;
+    my $default_numbubbles = 10;
+    if (($env{'form.bubbles_per_row'} =~ /^\d+$/) &&
+        ($env{'form.bubbles_per_row'} > 0)) {
+        $bubbles_per_row = $env{'form.bubbles_per_row'};
+    } else {
+        $bubbles_per_row = $default_numbubbles;
+    }
+    my $numrows = int ($numitems/$bubbles_per_row);
+    if (($numitems % $bubbles_per_row) != 0) {
+        $numrows ++;
+    }
+    return ($numrows,$bubbles_per_row);
+}
+
 1;
 __END__