Exemplo n.º 1
0
void
SyGAddRecentFile
	(
	const JCharacter* fullname
	)
{
	JString recentDir;
	JString filename;
	JString path;
	if (SyGGetRecentFileDirectory(&recentDir) &&
		JSplitPathAndName(fullname, &path, &filename))
		{
		const JString recentFile = JCombinePathAndName(recentDir, filename);
		if (JNameUsed(recentFile))
			{
			JRemoveFile(recentFile);
			JCreateSymbolicLink(fullname, recentFile);
			return;
			}

		// remove oldest links such that only kRecentFileCount - 1 remain

		JDirInfo* info;
		if (JDirInfo::Create(recentDir, &info))
			{
			JBoolean changed = kJFalse;

			JSize count = info->GetEntryCount();
			for (JIndex i=1; i<=count; i++)
				{
				if (info->GetEntry(i).IsBrokenLink())
					{
					JRemoveFile(info->GetEntry(i).GetFullName());
					changed = kJTrue;
					}
				}

			if (changed)
				{
				info->ForceUpdate();
				}

			count = info->GetEntryCount();
			if (count >= kRecentFileCount)
				{
				info->ChangeSort(JDirEntry::CompareModTimes, JOrderedSetT::kSortDescending);
				for (JIndex i=count; i>=kRecentFileCount; i--)
					{
					JRemoveFile(info->GetEntry(i).GetFullName());
					}
				}

			// add new entry

			JCreateSymbolicLink(fullname, recentFile);
			}
		}
}
Exemplo n.º 2
0
JBoolean
JXImageSelection::GetImage
	(
	JXSelectionManager* selMgr,
	const Atom			selectionName,
	const Time			time,
	JXColormap*			colormap,
	JXImage**			image,
	const JBoolean		allowApproxColors
	)
{
	JXDisplay* display   = colormap->GetDisplay();
	const Atom xpmXAtom  = display->RegisterXAtom(kXPMXAtomName);
	const Atom gifXAtom  = display->RegisterXAtom(kGIFXAtomName);
	const Atom pngXAtom  = display->RegisterXAtom(kPNGXAtomName);
	const Atom jpegXAtom = display->RegisterXAtom(kJPEGXAtomName);

	JArray<Atom> typeList;
	if (selMgr->GetAvailableTypes(selectionName, time, &typeList))
		{
		JBoolean xpm=kJFalse, gif=kJFalse, png=kJFalse, jpeg=kJFalse;

		const JSize count = typeList.GetElementCount();
		for (JIndex i=1; i<=count; i++)
			{
			const Atom type = typeList.GetElement(i);
			xpm  = JI2B( xpm  || type == xpmXAtom  );
			gif  = JI2B( gif  || type == gifXAtom  );
			png  = JI2B( png  || type == pngXAtom  );
			jpeg = JI2B( jpeg || type == jpegXAtom );
			}

		while (xpm || gif || png || jpeg)
			{
			Atom type = None;
			if (png)
				{
				png  = kJFalse;
				type = pngXAtom;
				}
			else if (gif)
				{
				gif  = kJFalse;
				type = gifXAtom;
				}
			else if (xpm)
				{
				xpm  = kJFalse;
				type = xpmXAtom;
				}
			else if (jpeg)		// JPEG is lossy
				{
				jpeg = kJFalse;
				type = jpegXAtom;
				}

			Atom returnType;
			unsigned char* data;
			JSize dataLength;
			JXSelectionManager::DeleteMethod delMethod;
			JString fileName;
			if (selMgr->GetData(selectionName, time, type,
								&returnType, &data, &dataLength, &delMethod) &&
				(JCreateTempFile(&fileName)).OK())
				{
				ofstream output(fileName);
				output.write((char*) data, dataLength);
				output.close();

				selMgr->DeleteData(&data, delMethod);

				const JError err =
					JXImage::CreateFromFile(display, colormap, fileName, image,
											allowApproxColors);
				JRemoveFile(fileName);

				if (err.OK())
					{
					return kJTrue;
					}
				}
			}
		}

	*image = NULL;
	return kJFalse;
}
Exemplo n.º 3
0
JBoolean
JXImageSelection::ConvertData
	(
	const Atom		requestType,
	Atom*			returnType,
	unsigned char**	data,
	JSize*			dataLength,
	JSize*			bitsPerBlock
	)
	const
{
	*data         = NULL;
	*dataLength   = 0;
	*returnType   = None;
	*bitsPerBlock = 8;

	if (itsImage == NULL)
		{
		return kJFalse;
		}

	JString fileName;
	if (!(JCreateTempFile(&fileName)).OK())
		{
		return kJFalse;
		}

	JError err = JUnknownError(1);
	if (requestType == itsXPMXAtom)
		{
		err = itsImage->WriteXPM(fileName);
		}
	else if (requestType == itsGIFXAtom)
		{
		err = itsImage->WriteGIF(fileName, kJFalse);	// if too many colors, use PNG
		}
	else if (requestType == itsPNGXAtom)
		{
		err = itsImage->WritePNG(fileName);
		}
	else if (requestType == itsJPEGXAtom)
		{
		err = itsImage->WriteJPEG(fileName);
		}

	if (err.OK())
		{
		JString imageData;
		JReadFile(fileName, &imageData);
		JRemoveFile(fileName);

		*returnType = requestType;
		*dataLength = imageData.GetLength();
		*data       = new unsigned char[ *dataLength ];
		if (*data != NULL)
			{
			memcpy(*data, imageData.GetCString(), *dataLength);
			return kJTrue;
			}
		}

	JRemoveFile(fileName);
	return kJFalse;
}
void
GenerateCode
	(
	istream&			input,
	ostream&			output,
	const JString&		stringPath,
	const JString&		formName,
	const JString&		tagName,
	const JString&		userTopEnclVarName,
	JPtrArray<JString>*	objTypes,
	JPtrArray<JString>*	objNames
	)
{
JIndex i;

	// width

	input >> ws;
	JString line = JReadUntilws(input);
	assert( line == kFormWidthMarker );
	JSize formWidth;
	input >> formWidth;

	// height

	input >> ws;
	line = JReadUntilws(input);
	assert( line == kFormHeightMarker );
	JSize formHeight;
	input >> formHeight;

	// object count (marker contains whitespace)

	input >> ws;
	line = JReadUntil(input, ':') + ":";
	assert( line == kFormObjCountMarker );
	JSize itemCount;
	input >> itemCount;

	// create window

	const JString topEnclFrameName = tagName + "_Frame";
	const JString topEnclApName    = tagName + "_Aperture";

	JString topEnclVarName;
	if (tagName == kDefaultDelimTag)
		{
		topEnclVarName = kDefTopEnclVarName;

		output << "    JXWindow* window = new JXWindow(this, ";
		output << formWidth << ',' << formHeight;
		output << ", \"\");" << endl;
		output << "    assert( window != NULL );" << endl;
		output << endl;
		}
	else
		{
		assert( !userTopEnclVarName.IsEmpty() );
		topEnclVarName = userTopEnclVarName;

		output << "    const JRect ";
		topEnclFrameName.Print(output);
		output << "    = ";
		topEnclVarName.Print(output);
		output << "->GetFrame();" << endl;

		output << "    const JRect ";
		topEnclApName.Print(output);
		output << " = ";
		topEnclVarName.Print(output);
		output << "->GetAperture();" << endl;

		output << "    ";
		topEnclVarName.Print(output);
		output << "->AdjustSize(" << formWidth << " - ";
		topEnclApName.Print(output);
		output << ".width(), " << formHeight << " - ";
		topEnclApName.Print(output);
		output << ".height());" << endl;

		output << endl;
		}

	// We need to calculate the enclosure for each object.  Since objects
	// are drawn in the order added, an object must come after its enclosure
	// in the list in order to be visible.

	JArray<JRect>    rectList(10);
	JArray<JBoolean> isInstanceVar(10);

	// This array is used to send the options to ApplyOptions.
	// It does not own the pointers that it contains.

	JPtrArray<JString> optionValues(JPtrArrayT::kForgetAll, kOptionCount);
	for (i=1; i<=kOptionCount; i++)
		{
		optionValues.Append(NULL);
		}

	// generate code for each object

	JStringManager stringMgr;

	JIndex objCount = 1;
	for (i=1; i<=itemCount; i++)
		{
		// check for start-of-object

		input >> ws;
		line = JReadLine(input);
		assert( line == kBeginObjLine );

		// object class

		JString flClass = JReadLine(input);
		RemoveIdentifier(kObjClassMarker, &flClass);

		// object type

		JString flType = JReadLine(input);
		RemoveIdentifier(kObjTypeMarker, &flType);

		// object frame

		input >> ws;
		line = JReadUntilws(input);
		assert( line == kObjRectMarker );
		JCoordinate x,y,w,h;
		input >> x >> y >> w >> h >> ws;
		const JRect frame(y, x, y+h, x+w);
		rectList.AppendElement(frame);

		// box type

		JString boxType = JReadLine(input);
		RemoveIdentifier(kObjBoxTypeMarker, &boxType);

		// colors

		input >> ws;
		line = JReadUntilws(input);
		assert( line == kObjColorsMarker );
		JString col1 = JReadUntilws(input);
		optionValues.SetElement(kCol1Index, &col1, JPtrArrayT::kForget);
		JString col2 = JReadUntilws(input);
		optionValues.SetElement(kCol2Index, &col2, JPtrArrayT::kForget);

		// label info

		JString lAlign = JReadLine(input);
		RemoveIdentifier(kObjLAlignMarker, &lAlign);
		JString lStyle = JReadLine(input);
		RemoveIdentifier(kObjLStyleMarker, &lStyle);
		JString lSize  = JReadLine(input);
		RemoveIdentifier(kObjLSizeMarker, &lSize);
		JString lColor = JReadLine(input);
		RemoveIdentifier(kObjLColorMarker, &lColor);
		JString label  = JReadLine(input);
		RemoveIdentifier(kObjLabelMarker, &label);

		// shortcuts

		JString shortcuts = JReadLine(input);
		RemoveIdentifier(kObjShortcutMarker, &shortcuts);
		optionValues.SetElement(kShortcutsIndex, &shortcuts, JPtrArrayT::kForget);

		// resizing (ignored)

		JIgnoreLine(input);

		// gravity

		input >> ws;
		line = JReadUntilws(input);
		assert( line == kObjGravityMarker );
		const JString nwGravity = JReadUntilws(input);
		const JString seGravity = JReadUntilws(input);

		// variable name

		JBoolean isLocal = kJFalse;
		JString* varName = new JString(JReadLine(input));
		assert( varName != NULL );
		RemoveIdentifier(kObjNameMarker, varName);
		if (varName->IsEmpty())
			{
			isInstanceVar.AppendElement(kJFalse);
			GetTempVarName(tagName, varName, *objNames);
			isLocal = kJTrue;
			}
		else if (varName->GetFirstCharacter() == '(' &&
				 varName->GetLastCharacter()  == ')')
			{
			isInstanceVar.AppendElement(kJFalse);
			isLocal  = kJTrue;
			*varName = varName->GetSubstring(2, varName->GetLength()-1);
			}
		else if (varName->GetFirstCharacter() == '<' &&
				 varName->GetLastCharacter()  == '>')
			{
			isInstanceVar.AppendElement(kJFalse);
			*varName = varName->GetSubstring(2, varName->GetLength()-1);
			}
		else
			{
			isInstanceVar.AppendElement(kJTrue);
			}
		objNames->Append(varName);

		// callback (ignored)

		JIgnoreLine(input);

		// callback argument

		JString cbArg = JReadLine(input);
		RemoveIdentifier(kObjCBArgMarker, &cbArg);

		JString cbArgExtra;
		do
			{
			cbArgExtra = JReadLine(input);
			cbArgExtra.TrimWhitespace();
			}
			while (!cbArgExtra.IsEmpty());

		// don't bother to generate code for initial box
		// if it is FL_BOX, FLAT_BOX, FL_COL1

		if (i==1 && flClass == "FL_BOX" && flType == "FLAT_BOX" && col1 == "FL_COL1")
			{
			rectList.RemoveElement(objCount);
			isInstanceVar.RemoveElement(objCount);
			objNames->DeleteElement(objCount);
			continue;
			}

		// check for errors -- safe since we have read in entire object

		JString hSizing, vSizing;
		if (!ParseGravity(nwGravity, &hSizing, &vSizing))
			{
			cerr << "Illegal sizing specification ";
			cerr << nwGravity << ',' << seGravity;
			cerr << " for '" << *varName << '\'' << endl;
			rectList.RemoveElement(objCount);
			isInstanceVar.RemoveElement(objCount);
			objNames->DeleteElement(objCount);
			continue;
			}

		if (*varName == topEnclVarName)
			{
			cerr << "Cannot use reserved name '" << topEnclVarName << '\'' << endl;
			rectList.RemoveElement(objCount);
			isInstanceVar.RemoveElement(objCount);
			objNames->DeleteElement(objCount);
			continue;
			}

		// get the object's enclosure

		JIndex enclIndex;
		JString enclName;
		JRect localFrame = frame;
		if (GetEnclosure(rectList, objCount, &enclIndex))
			{
			enclName = *(objNames->NthElement(enclIndex));
			const JRect enclFrame = rectList.GetElement(enclIndex);
			localFrame.Shift(-enclFrame.topLeft());
			}
		else
			{
			enclName = topEnclVarName;
			}

		// get the class name and additional arguments

		JString* className = new JString;
		assert( className != NULL );
		objTypes->Append(className);

		JString argList;
		if (!GetConstructor(flClass, flType, &label, className, &argList))
			{
			cerr << "Unsupported class: " << flClass << ", " << flType << endl;
			rectList.RemoveElement(objCount);
			isInstanceVar.RemoveElement(objCount);
			objNames->DeleteElement(objCount);
			objTypes->DeleteElement(objCount);
			continue;
			}

		// generate the actual code

		const JBoolean needCreate = NeedsCreateFunction(*className);

		output << "    ";
		if (isLocal)
			{
			className->Print(output);
			output << "* ";
			}
		varName->Print(output);
		output << " =" << endl;
		output << "        ";
		if (!needCreate)
			{
			output << "new ";
			}
		className->Print(output);
		if (needCreate)
			{
			output << "::Create";
			}
		output << '(';
		if (!argList.IsEmpty())
			{
			argList.Print(output);
			if (argList.GetLastCharacter() != ',')
				{
				output << ',';
				}
			output << ' ';
			}

		if (!cbArg.IsEmpty())
			{
			cbArg.Print(output);
			if (cbArg.GetLastCharacter() != ',')
				{
				output << ',';
				}
			output << ' ';
			}

		if ((*className == "JXStaticText" && cbArg.IsEmpty()) ||
			NeedsStringArg(*className))
			{
			JString id = *varName;
			id += "::";
			id += formName;
			id += "::";
			id += tagName;		// last since it is almost always the same

			output << "JGetString(\"";
			id.Print(output);
			output << "\"), ";

			stringMgr.SetElement(id, label, JPtrArrayT::kDelete);
			}

		enclName.Print(output);
		output << ',' << endl;
		output << "                    JXWidget::";
		hSizing.Print(output);
		output << ", JXWidget::";
		vSizing.Print(output);
		output << ", " << localFrame.left << ',' << localFrame.top << ", ";
		output << localFrame.width() << ',' << localFrame.height() << ");" << endl;

		output << "    assert( ";
		varName->Print(output);
		output << " != NULL );" << endl;

		ApplyOptions(output, *className, formName, tagName, *varName, optionValues,
					 lSize, lStyle, lColor, &stringMgr);

		if (*className == "JXStaticText" && cbArg.IsEmpty() &&
			!lAlign.Contains("FL_ALIGN_TOP") && localFrame.height() <= 20)
			{
			output << "    ";
			varName->Print(output);
			output << "->SetToLabel();" << endl;
			}

		output << endl;

		// now we know the object is valid

		objCount++;
		}

	// write string database

	JString dbFileName = stringPath + formName;
	if (tagName != kDefaultDelimTag)
		{
		dbFileName += kCustomTagMarker + tagName;
		}
	dbFileName += "_layout";

	if (stringMgr.GetElementCount() > 0)
		{
		JEditVCS(dbFileName);
		ofstream dbOutput(dbFileName);
		stringMgr.WriteFile(dbOutput);
		}
	else
		{
		JRemoveVCS(dbFileName);
		JRemoveFile(dbFileName);
		}

	// reset enclosure size

	if (tagName != kDefaultDelimTag)
		{
		output << "    ";
		topEnclVarName.Print(output);
		output << "->SetSize(";
		topEnclFrameName.Print(output);
		output << ".width(), ";
		topEnclFrameName.Print(output);
		output << ".height());" << endl;
		output << endl;
		}

	// throw away temporary variables

	objCount--;
	assert( objCount == isInstanceVar.GetElementCount() );
	assert( objCount == objTypes->GetElementCount() );
	assert( objCount == objNames->GetElementCount() );
	for (i=objCount; i>=1; i--)
		{
		if (!isInstanceVar.GetElement(i))
			{
			objTypes->DeleteElement(i);
			objNames->DeleteElement(i);
			}
		}
}
Exemplo n.º 5
0
JBoolean
GMApp::OpenSession()
{
	JBoolean success = kJFalse;

	JString homeDir;
	if (JGetHomeDirectory(&homeDir))
		{
		JAppendDirSeparator(&homeDir);
		JString session = homeDir + kStateFileName;
		if (JFileExists(session) && JFileReadable(session))
			{
			JFileArray* fileArray;
			JError err = JFileArray::Create(session, kStateFileSignature, &fileArray);
			if (err.OK())
				{
				JFileVersion version = fileArray->GetVersion();
				if (0 < version && version <= kCurrentSessionVersion)
					{
					JFAID id = 1;
					JSize count;
					{
					std::string data;
					fileArray->GetElement(id, &data);
					std::istringstream is(data);
					is >> count;
					id.SetID(id.GetID() + 1);
					}

					if (count == 0)
						{
						delete fileArray;
						JRemoveFile(session);
						return kJFalse;
						}

					for (JSize i = 1; i <= count; i++)
						{
						JFileArray* embed;
						err = JFileArray::Create(fileArray, id, &embed);
						GMessageTableDir* dir;
						if (GMessageTableDir::Create(this, *embed, &dir, version))
							{
							itsTableDirs->Append(dir);
							}
						id.SetID(id.GetID() + 1);
						delete embed;
						}

					{
					std::string data;
					fileArray->GetElement(id, &data);
					std::istringstream is(data);
					is >> count;
					id.SetID(id.GetID() + 1);
					}

					for (JSize i = 1; i <= count; i++)
						{
						std::string data;
						fileArray->GetElement(id, &data);
						std::istringstream is(data);
						GMessageEditDir* dir = new GMessageEditDir(JXGetApplication());
						assert(dir != NULL);
						dir->ReadState(is, version);
						dir->Activate();
						id.SetID(id.GetID() + 1);
						}

					{
					std::string data;
					fileArray->GetElement(id, &data);
					std::istringstream is(data);
					GGetInboxMgr()->ReadState(is);
					}

					success = kJTrue;
					}
Exemplo n.º 6
0
void
GMApp::SaveState()
{
	JString homeDir;
	if (JGetHomeDirectory(&homeDir))
		{
		JAppendDirSeparator(&homeDir);
		JString session = homeDir + kStateFileName;
		if (JFileExists(session))
			{
			JRemoveFile(session);
			}
		JFileArray* fileArray;
		JError err = JFileArray::Create(session, kStateFileSignature, &fileArray);
		if (!err.OK())
			{
			if (JFileExists(session))
				{
				JRemoveFile(session);
				}
			delete fileArray;
			return;
			}
		fileArray->SetVersion(kCurrentSessionVersion);

		JSize count;
		{
		std::ostringstream dataStream;
		count = itsTableDirs->GetElementCount();
		dataStream << count;
		fileArray->AppendElement(dataStream);
		}

		JFAID_t id = 2;
		for (JSize i = 1; i <= count; i++)
			{
			JFileArray* embed;
			err = JFileArray::Create(fileArray, id, &embed);
			itsTableDirs->NthElement(i)->GetData()->Save();
			itsTableDirs->NthElement(i)->SaveState(*embed);
			delete embed;
			id ++;
			}

		for (JSize i = itsEditDirs->GetElementCount(); i >= 1; i--)
			{
			itsEditDirs->NthElement(i)->CheckForPendingMessage();
			}

		{
		std::ostringstream dataStream;
		count = itsEditDirs->GetElementCount();
		dataStream << count;
		fileArray->AppendElement(dataStream);
		}

		for (JSize i = 1; i <= count; i++)
			{
			std::ostringstream dataStream;
			itsEditDirs->NthElement(i)->SaveState(dataStream);
			fileArray->AppendElement(dataStream);
			}

		{
		std::ostringstream dataStream;
		GGetInboxMgr()->SaveState(dataStream);
		fileArray->AppendElement(dataStream);
		}

		delete fileArray;
		}
}
void
CBFunctionMenu::UpdateMenu()
{
	CBFnMenuUpdater* updater        = CBMGetFnMenuUpdater();
	const JXKeyModifiers& modifiers = GetDisplay()->GetLatestKeyModifiers();

	JBoolean sort = updater->WillSortFnNames();
	if (modifiers.meta())
		{
		sort = !sort;
		}

	JBoolean includeNS = updater->WillIncludeNamespace();
	if (modifiers.shift())
		{
		includeNS = !includeNS;
		}

	const JBoolean pack = updater->WillPackFnNames();

	if (itsNeedsUpdate || sort != itsSortFlag ||
		includeNS != itsIncludeNSFlag || pack != itsPackFlag)
		{
		#if defined CODE_CRUSADER

		assert( itsDoc != NULL );

		JString fileName;
		JBoolean deleteFile = kJFalse;
		if (itsDoc->NeedsSave())
			{
			itsDoc->SafetySave(JXDocumentManager::kTimer);
			if (!itsDoc->GetSafetySaveFileName(&fileName) &&
				(JCreateTempFile(&fileName)).OK())
				{
				// directory may not be writable

				deleteFile = kJTrue;

				// itsDoc can't be CBTextDocument, because Code Medic uses us

				CBTextDocument* textDoc = dynamic_cast<CBTextDocument*>(itsDoc);
				assert( textDoc != NULL );

				std::ofstream output(fileName);
				((textDoc->GetTextEditor())->GetText()).Print(output);
				}
			}
		else
			{
			JBoolean onDisk;
			fileName = itsDoc->GetFullName(&onDisk);
			if (!onDisk)
				{
				fileName.Clear();
				}
			}

		#elif defined CODE_MEDIC

		const JString& fileName = itsFileName;

		#endif

		if (!fileName.IsEmpty())
			{
			itsLang = updater->UpdateMenu(fileName, itsFileType, sort, includeNS, pack,
										  this, itsLineIndexList);
			}
		else
			{
			itsLang = kCBOtherLang;
			this->RemoveAllItems();
			itsLineIndexList->RemoveAll();
			}

		if (IsEmpty())
			{
			SetEmptyMenuItems();
			}

		itsNeedsUpdate    = kJFalse;
		itsSortFlag       = sort;
		itsIncludeNSFlag  = includeNS;
		itsPackFlag       = pack;
		itsCaretItemIndex = 0;				// nothing to remove
		itsTE->DeactivateCurrentUndo();		// force another TextChanged

		#if defined CODE_CRUSADER

		if (deleteFile)
			{
			JRemoveFile(fileName);
			}

		#endif
		}

	// mark caret location

	if (!sort && !itsLineIndexList->IsEmpty())
		{
		if (itsCaretItemIndex > 0)
			{
			ShowSeparatorAfter(itsCaretItemIndex, kJFalse);
			itsCaretItemIndex = 0;
			}

		const JIndex lineIndex =
			itsTE->VisualLineIndexToCRLineIndex(
						itsTE->GetLineForChar(itsTE->GetInsertionIndex()));

		const JSize count = GetItemCount();
		assert( count == itsLineIndexList->GetElementCount() );

		for (JIndex i=1; i<=count; i++)
			{
			if (itsLineIndexList->GetElement(i) > lineIndex)
				{
				itsCaretItemIndex = i-1;
				if (itsCaretItemIndex > 0)
					{
					ShowSeparatorAfter(itsCaretItemIndex, kJTrue);
					}
				break;
				}
			}
		}
}
Exemplo n.º 8
0
void
JStringManager::Register
	(
	const JCharacter*	signature,
	const JCharacter**	defaultData
	)
{
	JString tempFileName;
	const JError err = JCreateTempFile(&tempFileName);
	if (!err.OK())
		{
		return;
		}

	{
	std::ofstream tempFile(tempFileName);

	JIndex i = 0;
	while (defaultData[i] != NULL)
		{
		tempFile.write(defaultData[i], strlen(defaultData[i]));
		i++;
		}
	}
	MergeFile(tempFileName);
	JRemoveFile(tempFileName);

	if (!JStringEmpty(signature))
		{
		const JCharacter* lang = getenv("LANG");

		JString path[2];
		JGetJDataDirectories(kDataDirName, path, path+1);

		JString name1, name2;
		for (JIndex i=0; i<2; i++)
			{
			if (!path[i].IsEmpty() &&
				(JDirectoryReadable(path[i]) ||
				 JCreateDirectory(path[i]) == kJNoError))
				{
				name1 = JCombinePathAndName(path[i], signature);

				if (lang != NULL)
					{
					name2 = name1;
					name2.AppendCharacter('_');
					name2.Append(lang);

					if (MergeFile(name2))
						{
						break;
						}
					}

				if (MergeFile(name1))
					{
					break;
					}
				}
			}
		}
}
JBoolean
JPTPrinter::Print
	(
	const JCharacter*	text,
	ostream&			trueOutput
	)
{
	ostream* dataOutput  = &trueOutput;
	ofstream* tempOutput = NULL;
	JString tempName;
	if (itsPrintReverseOrderFlag)
		{
		if (!(JCreateTempFile(&tempName)).OK())
			{
			return kJFalse;
			}

		tempOutput = new ofstream(tempName);
		assert( tempOutput != NULL );
		if (tempOutput->bad())
			{
			delete tempOutput;
			JRemoveFile(tempName);
			return kJFalse;
			}
		dataOutput = tempOutput;
		}

	const JSize headerLineCount = GetHeaderLineCount();
	const JSize footerLineCount = GetFooterLineCount();
	assert( itsPageHeight > headerLineCount + footerLineCount );

	const JSize lineCountPerPage = itsPageHeight - headerLineCount - footerLineCount;

	JLatentPG pg;
	pg.VariableLengthProcessBeginning("Printing page...", kJTrue, kJFalse);
	JBoolean keepGoing = kJTrue;

	JUnsignedOffset i   = 0;
	JIndex pageIndex    = 0;
	JSize printCount    = 0;
	JSize textLineCount = 0;
	while (keepGoing && text[i] != '\0')
		{
		pageIndex++;
		const JBoolean shouldPrintPage =
			JI2B(itsFirstPageIndex <= pageIndex &&
				 (itsLastPageIndex == 0 || pageIndex <= itsLastPageIndex));

		std::ostringstream bitBucket;
		ostream* output = shouldPrintPage ? dataOutput : (&bitBucket);

		if (shouldPrintPage)
			{
			printCount++;
			if (printCount > 1)
				{
				*output << kPageSeparatorStr;
				}
			}

		if (headerLineCount > 0)
			{
			PrintHeader(*output, pageIndex);
			}

		JSize lineNumberWidth = 0;
		if (itsPrintLineNumberFlag)
			{
			const JString lastLineIndexStr(pageIndex * lineCountPerPage, 0);
			lineNumberWidth = lastLineIndexStr.GetLength();
			}

		JSize lineCount = 0;
		while (lineCount < lineCountPerPage && text[i] != '\0')
			{
			JSize col = 0;

			if (itsPrintLineNumberFlag)
				{
				const JString lineNumberStr(textLineCount+1, 0);
				const JSize spaceCount = lineNumberWidth - lineNumberStr.GetLength();
				for (JIndex j=1; j<=spaceCount; j++)
					{
					*output << ' ';
					}
				lineNumberStr.Print(*output);
				*output << kLineNumberMarginStr;

				col += lineNumberWidth + kLineNumberMarginWidth;
				}

			if (col >= itsPageWidth)	// insures progress, even in ludicrous boundary case
				{
				col = itsPageWidth - 1;
				}

			while (col < itsPageWidth && text[i] != '\n' && text[i] != '\0')
				{
				if (text[i] == '\t')
					{
					const JSize spaceCount = itsTabWidth - (col % itsTabWidth);
					for (JIndex j=1; j<=spaceCount; j++)
						{
						*output << ' ';
						}
					col += spaceCount;
					}
				else if (text[i] == kJFormFeedKey)
					{
					*output << ' ';
					col++;
					}
				else
					{
					*output << text[i];
					col++;
					}
				i++;
				}

			*output << '\n';
			if (text[i] == '\n')
				{
				i++;
				}

			lineCount++;
			textLineCount++;
			}

		if (footerLineCount > 0)
			{
			while (lineCount < lineCountPerPage)
				{
				*output << '\n';
				lineCount++;
				}

			PrintFooter(*output, pageIndex);
			}

		keepGoing = pg.IncrementProgress();
		}

	pg.ProcessFinished();

	if (itsPrintReverseOrderFlag)
		{
		delete tempOutput;
		if (keepGoing)
			{
			JString text;
			JReadFile(tempName, &text);
			InvertPageOrder(text, trueOutput);
			}
		JRemoveFile(tempName);
		}

	return keepGoing;
}