void
GLFitDescriptionList::SyncWithManager()
{
	RemoveAllRows();
	
	const JSize count = GetFitManager()->GetFitCount();
	AppendRows(count);

	itsNameList->DeleteAll();
	
	for (JIndex i=1; i<=count; i++)
		{
		const GLFitDescription& fd	= GetFitManager()->GetFitDescription(i);
		JString* str	= jnew JString(fd.GetFnName());
		assert(str != NULL);
		itsNameList->Append(str);

		const JCoordinate width = 2*kHMarginWidth + kIconWidth + 
			GetFontManager()->GetDefaultFont().GetStringWidth(*str);
		if (width > itsMinColWidth)
			{
			itsMinColWidth = width;
			}
		}

	AdjustColWidth();
}
GLFitDescriptionList::GLFitDescriptionList
	(
	JXScrollbarSet*		scrollbarSet,
	JXContainer*		enclosure,
	const HSizingOption	hSizing,
	const VSizingOption	vSizing,
	const JCoordinate	x,
	const JCoordinate	y,
	const JCoordinate	w,
	const JCoordinate	h
	)
	:
	JXEditTable(1, kDefColWidth, scrollbarSet, enclosure, hSizing,vSizing, x,y, w,h),
	itsInput(NULL)
{
	itsMinColWidth = 1;

	const JFontManager* fontMgr = GetFontManager();
	const JSize rowHeight = 2*kVMarginWidth + fontMgr->GetDefaultFont().GetLineHeight();
	SetDefaultRowHeight(rowHeight);

	const JSize count = GetFitManager()->GetFitCount();

	itsNameList = jnew JPtrArray<JString>(JPtrArrayT::kDeleteAll);
	assert(itsNameList != NULL);

	AppendCols(1);
	SyncWithManager();

	JXColormap* colormap = GetColormap();
	SetRowBorderInfo(0, colormap->GetBlackColor());
	SetColBorderInfo(0, colormap->GetBlackColor());

	itsBuiltInIcon	= jnew JXImage(GetDisplay(), JXPM(glBuiltInFit));
	assert(itsBuiltInIcon != NULL);
	itsBuiltInIcon->ConvertToRemoteStorage();

	itsNonLinearIcon	= jnew JXImage(GetDisplay(), JXPM(glNonLinearFit));
	assert(itsNonLinearIcon != NULL);
	itsNonLinearIcon->ConvertToRemoteStorage();

	itsPolyIcon	= jnew JXImage(GetDisplay(), JXPM(glPolyFit));
	assert(itsPolyIcon != NULL);
	itsPolyIcon->ConvertToRemoteStorage();

	itsExecutableIcon = jnew JXImage(GetDisplay(), JXPM(jx_executable_small));
	assert( itsExecutableIcon != NULL );
	itsExecutableIcon->ConvertToRemoteStorage();

	ListenTo(GetFitManager());
}
void
GLFitDirector::AddHistoryText
	(
	const JBoolean refit
	)
{
	JString str;
	if (refit)
		{
		str	= "Refit\n";
		}
	else
		{
		str	= "Fit\n";
		}
	itsHistory->AppendText(str, kJFalse);
	JIndex index;
	JBoolean ok	= itsCurveList->GetCurrentCurveIndex(&index);
	assert(ok);
	str	= "Curve: " + itsPlot->GetCurveName(index) + "\n";
	itsHistory->AppendText(str, kJFalse);
	ok	= itsFitList->GetCurrentFitIndex(&index);
	assert(ok);
	const GLFitDescription& fd	= GetFitManager()->GetFitDescription(index);
	str	= "Fit type: " + fd.GetFnName() + "\n";
	itsHistory->AppendText(str, kJFalse);
	str.Clear();
	itsParameterTable->GetValueString(&str);
	itsHistory->AppendText(str, kJFalse);
	str = "Reduced Chi^2: " + itsChiSq->GetText() + "\n";
	itsHistory->AppendText(str, kJFalse);
	itsHistory->AppendText("\n\n", kJFalse);
}
void
GLFitDirector::TestFit()
{
	RemoveFit();
	JIndex index;
	JBoolean ok	= itsCurveList->GetCurrentCurveIndex(&index);
	JPlotDataBase* data	= &(itsPlot->GetCurve(index));
	ok	= itsFitList->GetCurrentFitIndex(&index);
	assert(ok);
	GLFitDescription& fd	= GetFitManager()->GetFitDescription(index);
	const JArray<JFloat>& parms	= itsParameterTable->GetStartValues();
	const JSize count	= parms.GetElementCount();
	for (JIndex i = 1; i <= count; i++)
		{
		fd.GetVarList()->SetValue(i + 1, parms.GetElement(i));
		}
	JFloat xmin, xmax;
	data->GetXRange(&xmin, &xmax);
	ok	= J2DPlotJFunction::Create(&itsTestFunction, itsFitPlot, fd.GetVarList(),
			fd.GetFitFunctionString(), 1, xmin, xmax);
	if (ok)
		{
		itsFitPlot->AddCurve(itsTestFunction, kJFalse, fd.GetFnName());
		}

}
void
GLFitDirector::UpdateFitMenu()
{
	JIndex index;
	if (itsCurveList->GetCurrentCurveIndex(&index) &&
		itsFitList->GetCurrentFitIndex(&index))
		{
		itsFitMenu->EnableItem(kFitCmd);
		const GLFitDescription& fd	= GetFitManager()->GetFitDescription(index);
		if (fd.RequiresStartValues())
			{
			itsFitMenu->EnableItem(kTestFitCmd);
			itsFitMenu->CheckItem(kShowStartCmd);
			itsFitMenu->DisableItem(kShowStartCmd);
			if (itsCurrentFit != NULL)
				{
				itsFitMenu->EnableItem(kRefitCmd);
				}
			}
		else if (fd.CanUseStartValues())
			{
			itsFitMenu->EnableItem(kShowStartCmd);
			if (itsParameterTable->IsShowingStartCol())
				{
				itsFitMenu->CheckItem(kShowStartCmd);
				itsFitMenu->EnableItem(kTestFitCmd);
				}
			if (itsCurrentFit != NULL)
				{
				itsFitMenu->EnableItem(kRefitCmd);
				}
			}
		if (GetFitManager()->FitIsRemovable(index))
			{
			itsFitMenu->EnableItem(kRemoveFitCmd);
			}
		if (itsCurrentFit != NULL)
			{
			itsFitMenu->EnableItem(kPlotCmd);
			itsFitMenu->EnableItem(kPrintCmd);
			}
		}
	itsFitMenu->EnableItem(kNonLinearCmd);
	itsFitMenu->EnableItem(kPolyCmd);
	itsFitMenu->EnableItem(kShowHistoryCmd);
	itsFitMenu->EnableItem(kCloseCmd);
}
void
GLFitDescriptionList::Receive
	(
	JBroadcaster* 	sender, 
	const Message&	message
	)
{
	if (sender == GetFitManager() && message.Is(GLFitManager::kFitsChanged))
		{
		SyncWithManager();
		}
}
void
GLFitDescriptionList::TableDrawCell
	(
	JPainter&		p,
	const JPoint&	cell,
	const JRect&	rect
	)
{
	HilightIfSelected(p, cell, rect);

	const JString* curveName = itsNameList->NthElement(cell.y);

	const GLFitDescription& fd	= GetFitManager()->GetFitDescription(cell.y);

	JRect irect	= rect;
	irect.right	= rect.left + kIconWidth;
	if (fd.GetType()	== GLFitDescription::kPolynomial)
		{
		p.Image(*itsPolyIcon, itsPolyIcon->GetBounds(), irect);
		}
	else if (fd.GetType()	== GLFitDescription::kNonLinear)
		{
		p.Image(*itsNonLinearIcon, itsNonLinearIcon->GetBounds(), irect);
		}
	else if (fd.GetType()	== GLFitDescription::kModule)
		{
		p.Image(*itsExecutableIcon, itsExecutableIcon->GetBounds(), irect);
		}
	else if (fd.GetType()	>= GLFitDescription::kBLinear)
		{
		p.Image(*itsBuiltInIcon, itsBuiltInIcon->GetBounds(), irect);
		}
	
	JRect r = rect;
	r.left += kHMarginWidth + kIconWidth;
	p.String(r, *curveName, JPainter::kHAlignLeft, JPainter::kVAlignCenter);
}
void
GLFitDirector::Fit()
{
	JXGetApplication()->DisplayBusyCursor();
	RemoveFit();
	JIndex index;
	JBoolean ok	= itsCurveList->GetCurrentCurveIndex(&index);
	JPlotDataBase* data	= &(itsPlot->GetCurve(index));
	ok	= itsFitList->GetCurrentFitIndex(&index);
	assert(ok);
	const GLFitDescription& fd	= GetFitManager()->GetFitDescription(index);
	if (fd.GetType() == GLFitDescription::kPolynomial)
		{
		JArray<JIndex> powers;
		const GLPolyFitDescription& pd	= dynamic_cast<const GLPolyFitDescription&>(fd);
		pd.GetPowers(&powers);
		assert(ok);

		JFloat xmax, xmin, ymax, ymin;
		JPlotFitLinearEq* fit;
		if (itsFitPlot->IsUsingRange())
			{
			itsFitPlot->GetRange(&xmin, &xmax, &ymin, &ymax);
			fit = new JPlotFitLinearEq(itsFitPlot, data, xmin, xmax, ymin, ymax);
			assert(fit != NULL);
			}
		else
			{
			data->GetXRange(&xmin, &xmax);
			fit = new JPlotFitLinearEq(itsFitPlot, data, xmin, xmax);
			assert(fit != NULL);
			}
		fit->InitializePolynomial(powers);
		if (itsParameterTable->IsShowingStartCol())
			{
			JVector p(const_cast<GLFitDescription&>(fd).GetVarList()->GetVariableCount() - 1);
			const JArray<JFloat>& parms	= itsParameterTable->GetStartValues();
			const JSize count	= p.GetDimensionCount();
			for (JIndex i = 1; i <= count; i++)
				{
				p.SetElement(i, parms.GetElement(i));
				}
			fit->JPlotFitBase::GenerateFit(p, 0);
			}
		else
			{
			fit->GenerateFit();
			}
		itsCurrentFit = fit;
		}
	else if (fd.GetType() == GLFitDescription::kNonLinear)
		{
		GLNonLinearFitDescription& nd	= 
			dynamic_cast<GLNonLinearFitDescription&>(const_cast<GLFitDescription&>(fd));

		JFloat xmax, xmin, ymax, ymin;
		JPlotFitNonLinear* fit;
		if (itsFitPlot->IsUsingRange())
			{
			itsFitPlot->GetRange(&xmin, &xmax, &ymin, &ymax);
			fit = new JPlotFitNonLinear(itsFitPlot, data, xmin, xmax, ymin, ymax);
			assert(fit != NULL);
			}
		else
			{
			data->GetXRange(&xmin, &xmax);
			fit = new JPlotFitNonLinear(itsFitPlot, data, xmin, xmax);
			assert(fit != NULL);
			}
		fit->SetVarList(nd.GetVarList());
		fit->SetFunction(nd.GetFunctionString());
		JString fp	= nd.GetFunctionPrimedString();
		if (!fp.IsEmpty())
			{
			fit->SetFPrimed(fp);
			}		
		JVector p(nd.GetVarList()->GetVariableCount() - 1);
		const JArray<JFloat>& parms	= itsParameterTable->GetStartValues();
		const JSize count	= p.GetDimensionCount();
		for (JIndex i = 1; i <= count; i++)
			{
			p.SetElement(i, parms.GetElement(i));
			}
		fit->SetInitialParameters(p);
		itsCurrentFit = fit;
		}
	else if (fd.GetType() == GLFitDescription::kBLinear)
		{
		JFloat xmax, xmin, ymax, ymin;
		JPlotFitLinear* fit;
		if (itsFitPlot->IsUsingRange())
			{
			itsFitPlot->GetRange(&xmin, &xmax, &ymin, &ymax);
			fit = new JPlotFitLinear(itsFitPlot, data, xmin, xmax, ymin, ymax);
			assert(fit != NULL);
			}
		else
			{
			data->GetXRange(&xmin, &xmax);
			fit = new JPlotFitLinear(itsFitPlot, data, xmin, xmax);
			assert(fit != NULL);
			}
		if (itsParameterTable->IsShowingStartCol())
			{
			JVector p(const_cast<GLFitDescription&>(fd).GetVarList()->GetVariableCount() - 1);
			const JArray<JFloat>& parms	= itsParameterTable->GetStartValues();
			const JSize count	= p.GetDimensionCount();
			for (JIndex i = 1; i <= count; i++)
				{
				p.SetElement(i, parms.GetElement(i));
				}
			fit->JPlotFitBase::GenerateFit(p, 0);
			}
		else
			{
			fit->GenerateFit();
			}
		itsCurrentFit = fit;
		}
	else if (fd.GetType() == GLFitDescription::kBExp)
		{
		JFloat xmax, xmin, ymax, ymin;
		JPlotFitExp* fit;
		if (itsFitPlot->IsUsingRange())
			{
			itsFitPlot->GetRange(&xmin, &xmax, &ymin, &ymax);
			fit = new JPlotFitExp(itsFitPlot, data, xmin, xmax, ymin, ymax);
			assert(fit != NULL);
			}
		else
			{
			data->GetXRange(&xmin, &xmax);
			fit = new JPlotFitExp(itsFitPlot, data, xmin, xmax);
			assert(fit != NULL);
			}
		if (itsParameterTable->IsShowingStartCol())
			{
			JVector p(const_cast<GLFitDescription&>(fd).GetVarList()->GetVariableCount() - 1);
			const JArray<JFloat>& parms	= itsParameterTable->GetStartValues();
			const JSize count	= p.GetDimensionCount();
			for (JIndex i = 1; i <= count; i++)
				{
				p.SetElement(i, parms.GetElement(i));
				}
			fit->JPlotFitBase::GenerateFit(p, 0);
			}
		else
			{
			fit->GenerateFit();
			}
		itsCurrentFit = fit;
		}
	else if (fd.GetType() == GLFitDescription::kBPower)
		{
		JFloat xmax, xmin, ymax, ymin;
		JPlotFitPowerLaw* fit;
		if (itsFitPlot->IsUsingRange())
			{
			itsFitPlot->GetRange(&xmin, &xmax, &ymin, &ymax);
			fit = new JPlotFitPowerLaw(itsFitPlot, data, xmin, xmax, ymin, ymax);
			assert(fit != NULL);
			}
		else
			{
			data->GetXRange(&xmin, &xmax);
			fit = new JPlotFitPowerLaw(itsFitPlot, data, xmin, xmax);
			assert(fit != NULL);
			}
		if (itsParameterTable->IsShowingStartCol())
			{
			JVector p(const_cast<GLFitDescription&>(fd).GetVarList()->GetVariableCount() - 1);
			const JArray<JFloat>& parms	= itsParameterTable->GetStartValues();
			const JSize count	= p.GetDimensionCount();
			for (JIndex i = 1; i <= count; i++)
				{
				p.SetElement(i, parms.GetElement(i));
				}
			fit->JPlotFitBase::GenerateFit(p, 0);
			}
		else
			{
			fit->GenerateFit();
			}
		itsCurrentFit = fit;
		}
	else if (fd.GetType() == GLFitDescription::kModule)
		{
		GLModuleFitDescription& md	= 
			dynamic_cast<GLModuleFitDescription&>(const_cast<GLFitDescription&>(fd));
		JFloat xmax, xmin, ymax, ymin;
		JPlotFitModule* fit;
		if (itsFitPlot->IsUsingRange())
			{
			itsFitPlot->GetRange(&xmin, &xmax, &ymin, &ymax);
			fit = new JPlotFitModule(itsFitPlot, data, xmin, xmax, ymin, ymax);
			assert(fit != NULL);
			}
		else
			{
			data->GetXRange(&xmin, &xmax);
			fit = new JPlotFitModule(itsFitPlot, data, xmin, xmax);
			assert(fit != NULL);
			}
		fit->SetFitModule(md.GetFitModule());
		JVector p(md.GetParameterCount());
		const JArray<JFloat>& parms	= itsParameterTable->GetStartValues();
		const JSize count	= p.GetDimensionCount();
		for (JIndex i = 1; i <= count; i++)
			{
			p.SetElement(i, parms.GetElement(i));
			}
		fit->SetInitialParameters(p);
		itsCurrentFit = fit;
		}
	itsFitPlot->AddCurve(itsCurrentFit, kJFalse, fd.GetFnName());
	itsDiffPlot->AddCurve(itsCurrentFit->GetDiffData(), kJFalse, fd.GetFnName());
	const JSize count	= itsCurrentFit->GetParameterCount();
	for (JIndex i = 1; i <= count; i++)
		{
		JFloat value;
		JBoolean ok	= itsCurrentFit->GetParameter(i, &value);
		assert(ok);
		JFloat error = 0;
		itsCurrentFit->GetParameterError(i, &error);
		itsParameterTable->SetValue(i, value, error);			
		if (itsCurrentFit->GetGoodnessOfFit(&value))
			{
			itsChiSq->SetText(JString(value, JString::kPrecisionAsNeeded, JString::kStandardExponent, 0, 5));
			}
		else
			{
			itsChiSq->SetText("");
			}
		
		}
}
void  
GLFitDirector::HandleFitMenu
	(
	const JIndex index
	)
{
	if (index == kNonLinearCmd)
		{
		itsNLFitDialog	= new GLNonLinearFitDialog(this);
		assert(itsNLFitDialog != NULL);
		itsNLFitDialog->BeginDialog();
		ListenTo(itsNLFitDialog);
		}
	else if (index == kPolyCmd)
		{
		itsPolyFitDialog	= new GLPolyFitDialog(this);
		assert(itsPolyFitDialog != NULL);
		itsPolyFitDialog->BeginDialog();
		ListenTo(itsPolyFitDialog);
		}
	else if (index == kRemoveFitCmd)
		{
		JIndex index;
		if (itsFitList->GetCurrentFitIndex(&index))
			{
			itsExprWidget->ClearFunction();
			itsExprWidget->Hide();
			GetFitManager()->RemoveFitDescription(index);
			}
		}
	else if (index == kFitCmd)
		{
		Fit();
		AddHistoryText();
		}
	else if (index == kTestFitCmd)
		{
		TestFit();
		}
	else if (index == kShowStartCmd)
		{
		itsParameterTable->ShowStartCol(!itsParameterTable->IsShowingStartCol());
		}
	else if (index == kRefitCmd)
		{
		Refit();
		AddHistoryText(kJTrue);
		}
	else if (index == kPlotCmd)
		{
//		JString fn	= itsCurrentFit->GetFitFunctionString();
// 		need to check - via 
// 		JFunction* f;
//		if (JParseFunction(itsFunctionString->GetText(), itsList, &f))
//			{
//			return kJTrue;
//			}
//		whether or not the function is valid.
		
		JIndex index;
		JBoolean ok	= itsCurveList->GetCurrentCurveIndex(&index);
		JPlotDataBase* data	= &(itsPlot->GetCurve(index));
		assert(itsCurrentFit != NULL);
		JPlotFitProxy* proxy	= new JPlotFitProxy(itsCurrentFit, itsPlot, data);
		assert(proxy != NULL);
		JIndex findex;
		ok	= itsFitList->GetCurrentFitIndex(&findex);
		assert(ok);
		const GLFitDescription& fd	= GetFitManager()->GetFitDescription(findex);
		itsDir->AddFitProxy(proxy, index, fd.GetFnName());
		}
	else if (index == kShowHistoryCmd)
		{
		itsHistory->Activate();
		}
	else if (index == kPrintCmd)
		{
		itsPrinter->BeginUserPrintSetup();
		}
	else if (index == kCloseCmd)
		{
		Close();
		}
}
void
GLFitDirector::Receive
	(
	JBroadcaster*	sender,
	const Message&	message
	)
{
	if (sender == itsFitMenu && message.Is(JXMenu::kItemSelected))
		{
		const JXMenu::ItemSelected* selection =
			dynamic_cast<const JXMenu::ItemSelected*>(&message);
		assert( selection != NULL );
		HandleFitMenu(selection->GetIndex());
		}
	else if (sender == itsFitMenu && message.Is(JXMenu::kNeedsUpdate))
		{
		UpdateFitMenu();
		}
	else if (sender == itsPrefsMenu && message.Is(JXMenu::kItemSelected))
		{
		 const JXMenu::ItemSelected* selection =
			dynamic_cast<const JXMenu::ItemSelected*>(&message);
		assert( selection != NULL );
		HandlePrefsMenu(selection->GetIndex());
		}
	else if (sender == itsHelpMenu && message.Is(JXMenu::kItemSelected))
		{
		const JXMenu::ItemSelected* selection =
			dynamic_cast<const JXMenu::ItemSelected*>(&message);
		assert( selection != NULL );
		HandleHelpMenu(selection->GetIndex());
		}
	else if (sender == itsCurveList && message.Is(GLCurveNameList::kCurveSelected))
		{
		const GLCurveNameList::CurveSelected* info = 
			dynamic_cast<const GLCurveNameList::CurveSelected*>(&message);
		assert(info != NULL);
		JPlotDataBase& curve = itsPlot->GetCurve(info->GetIndex());

		RemoveFit();
		RemoveCurves();

		itsParameterTable->ClearValues();
		itsChiSq->SetText("");

		// add new curve.
		itsFitPlot->AddCurve(&curve, kJFalse, itsPlot->GetCurveName(info->GetIndex()));
		itsFitPlot->ProtectCurve(1, kJTrue);
		}
	else if (sender == itsFitList && message.Is(GLFitDescriptionList::kFitSelected))
		{
		const GLFitDescriptionList::FitSelected* info = 
			dynamic_cast<const GLFitDescriptionList::FitSelected*>(&message);
		assert(info != NULL);
		const GLFitDescription& fd	= GetFitManager()->GetFitDescription(info->GetIndex());
		itsParameterTable->SetFitDescription(fd);
		RemoveFit();
		itsChiSq->SetText("");
		JFunction* f;
		if (JParseFunction(fd.GetFitFunctionString(), const_cast<GLFitDescription&>(fd).GetVarList(), &f))
			{
			itsExprWidget->SetFunction(const_cast<GLFitDescription&>(fd).GetVarList(), f);
			itsExprWidget->Show();
			}		
		}
	else if (sender == itsFitList && message.Is(GLFitDescriptionList::kFitInitiated))
		{
		const GLFitDescriptionList::FitInitiated* info = 
			dynamic_cast<const GLFitDescriptionList::FitInitiated*>(&message);
		assert(info != NULL);
		const GLFitDescription& fd	= GetFitManager()->GetFitDescription(info->GetIndex());
		if (!itsParameterTable->BeginEditingStartValues())
			{
			Fit();
			AddHistoryText();
			}
		}
	else if (sender == itsParameterTable && message.Is(GLFitParameterTable::kValueChanged))
		{
		const GLFitParameterTable::ValueChanged* info =
			dynamic_cast<const GLFitParameterTable::ValueChanged*>(&message);
		assert(info != NULL);
		JIndex index;
		JBoolean ok	= itsFitList->GetCurrentFitIndex(&index);
		assert(ok);
		GLFitDescription& fd	= GetFitManager()->GetFitDescription(index);
		const JArray<JFloat>& parms	= itsParameterTable->GetStartValues();
		const JSize count	= parms.GetElementCount();
		for (JIndex i = 1; i <= count; i++)
			{
			fd.GetVarList()->SetValue(i + 1, parms.GetElement(i));
			}
		GetWindow()->Refresh();
		}
	else if (sender == itsNLFitDialog && message.Is(JXDialogDirector::kDeactivated))
		{
		const JXDialogDirector::Deactivated* info =
			dynamic_cast<const JXDialogDirector::Deactivated*>(&message);
		assert(info != NULL);
		if (info->Successful())
			{
			GLNonLinearFitDescription* fit	= 
				new GLNonLinearFitDescription(itsNLFitDialog->GetFitName(), 
					itsNLFitDialog->GetFunctionString(), 
					itsNLFitDialog->GetDerivativeString(),
					itsNLFitDialog->GetVarList().GetVariables());
			assert(fit != NULL);
			GetFitManager()->AddFitDescription(fit);
			}
		itsNLFitDialog	= NULL;	
		}
	else if (sender == itsPolyFitDialog && message.Is(JXDialogDirector::kDeactivated))
		{
		const JXDialogDirector::Deactivated* info =
			dynamic_cast<const JXDialogDirector::Deactivated*>(&message);
		assert(info != NULL);
		if (info->Successful())
			{
			JArray<JIndex> powers;
			itsPolyFitDialog->GetPowers(&powers);
			GLPolyFitDescription* fit	= 
				new GLPolyFitDescription(itsPolyFitDialog->GetFitName(), 
					powers);
			assert(fit != NULL);
			GetFitManager()->AddFitDescription(fit);
			}
		itsPolyFitDialog	= NULL;	
		}
	else if (sender == itsPrinter &&
			 message.Is(JPrinter::kPrintSetupFinished))
		{
		const JPrinter::PrintSetupFinished* info =
			dynamic_cast<const JPrinter::PrintSetupFinished*>(&message);
		assert(info != NULL);
		if (info->Successful())
			{
			Print();
			}
		}
	else
		{
		JXWindowDirector::Receive(sender, message);
		}
}
void
GLFitDirector::Print()
{
	if (itsPrinter->OpenDocument())
		{
		if (itsPrinter->NewPage())
			{
			JCoordinate kTopMargin	= 20;
			JCoordinate kLeftMargin	= 20;
			JCoordinate width		= (JCoordinate)(itsPrinter->GetPageWidth() * 0.8);
			JCoordinate tableD		= 70;
			JCoordinate kPlotSep	= 20;
			JCoordinate kExprX		= itsPrinter->GetPageWidth() - kLeftMargin - itsExprWidget->GetBoundsWidth();
			JCoordinate kLMargin	= (itsPrinter->GetPageWidth() - width)/2;

			// draw header info
			JString str	= "Curve: ";
			JIndex index;
			JBoolean ok	= itsCurveList->GetCurrentCurveIndex(&index);
			assert(ok);
			str	+= + itsPlot->GetCurveName(index);		
			itsPrinter->String(kLeftMargin, 0, str);

			str	= "Fit: ";
			ok	= itsFitList->GetCurrentFitIndex(&index);
			assert(ok);
			const GLFitDescription& fd	= GetFitManager()->GetFitDescription(index);
			str += fd.GetFnName();
			itsPrinter->String(kLeftMargin, kPlotSep, str);

			str = "Normalized chi-squared (chi^2/n-";
			str += JString(fd.GetParameterCount(), JString::kBase10) + "): ";
			str += itsChiSq->GetText();
			itsPrinter->String(kLeftMargin, kPlotSep*2, str);

			// draw expression widget
			JRect eRect	= itsExprWidget->GetPrintBounds();
			itsExprWidget->DrawForPrint(*itsPrinter, JPoint(itsPrinter->GetPageWidth() - eRect.width(), 0)); 

			if (eRect.height() > tableD)
				{
				tableD	= eRect.height() + 5;
				}

			// draw table
			itsParameterTable->DrawForPrint(*itsPrinter, JPoint(0, tableD), kJFalse, kJTrue);
			JCoordinate parmHeight = itsParameterTable->GetBoundsHeight() + itsParameterColHeader->GetBoundsHeight();

			// draw plots
			JCoordinate height = (itsPrinter->GetPageHeight() - tableD - parmHeight)/2;
			JRect r;//(0, 0, height, width);
			r.left		= kLMargin;
			r.right		= r.left + width;
			r.top		= tableD + parmHeight;
			r.bottom	= r.top + height;
//			itsPrinter->ShiftOrigin(kLMargin, tableD + parmHeight);
			itsFitPlot->Print(*itsPrinter);//, r);
			r.Shift(0, height);
			itsDiffPlot->Print(*itsPrinter);//, r);
			}
		itsPrinter->CloseDocument();
		}
}