SyGDuplicateProcess::SyGDuplicateProcess
	(
	SyGFileTreeTable*					table,
	const JPtrArray<SyGFileTreeNode>&	nodeList
	)
	:
	itsTable(table),
	itsNodeList(JPtrArrayT::kForgetAll),
	itsFullNameList(JPtrArrayT::kDeleteAll),
	itsProcess(NULL),
	itsShouldEditFlag(JI2B(nodeList.GetElementCount() == 1))
{
	(itsTable->GetTableSelection()).ClearSelection();
	ClearWhenGoingAway(itsTable, &itsTable);

	const JSize count = nodeList.GetElementCount();
	for (JIndex i=1; i<=count; i++)
		{
		SyGFileTreeNode* node = const_cast<SyGFileTreeNode*>(nodeList.NthElement(i));
		itsNodeList.Append(node);
		itsFullNameList.Append((node->GetDirEntry())->GetFullName());
		ListenTo(node);
		}

	ProcessNextFile();
}
void
SyGViewManPageDialog::ViewManPages
	(
	const JPtrArray<JString>& list
	)
{
	if (itsViewCmd == kDefaultViewCmd)
		{
		JString cmd = kDefaultViewBin;

		const JSize count = list.GetElementCount();
		for (JIndex i=1; i<=count; i++)
			{
			cmd += kDefaultViewArg;
			cmd += JPrepArgForExec(*(list.NthElement(i)));
			}

		JSimpleProcess::Create(cmd, kJTrue);
		}
	else
		{
		const JSize count = list.GetElementCount();
		for (JIndex i=1; i<=count; i++)
			{
			ViewManPage(*(list.NthElement(i)));
			}
		}
}
void
JWebBrowser::ShowFileLocations
	(
	const JPtrArray<JString>& fileList
	)
{
	if (fileList.IsEmpty())
		{
		return;
		}

	const JSize count = fileList.GetElementCount();
	if (itsShowFileLocationCmd.Contains("$"))
		{
		for (JIndex i=1; i<=count; i++)
			{
			ShowFileLocation(*(fileList.NthElement(i)));
			}
		}
	else
		{
		JString s = itsShowFileLocationCmd;
		for (JIndex i=1; i<=count; i++)
			{
			s += " '";
			s += *(fileList.NthElement(i));
			s += "'";
			}

		JSimpleProcess::Create(s, kJTrue);
		}
}
void
CBStringCompleter::CopySymbolsForLanguage
	(
	const CBLanguage lang
	)
{
	JPtrArray<CBProjectDocument>* docList =
		(CBGetDocumentManager())->GetProjectDocList();

	const JSize docCount = docList->GetElementCount();
	for (JIndex i=1; i<=docCount; i++)
		{
		const CBSymbolList* symbolList =
			((docList->NthElement(i))->GetSymbolDirector())->GetSymbolList();

		const JSize symbolCount = symbolList->GetElementCount();
		for (JIndex j=1; j<=symbolCount; j++)
			{
			CBLanguage l;
			CBSymbolList::Type type;
			JBoolean fullyQualifiedFileScope;
			const JString& symbolName =
				symbolList->GetSymbol(j, &l, &type, &fullyQualifiedFileScope);
			if (l == lang && !fullyQualifiedFileScope)
				{
				Add(const_cast<JString*>(&symbolName));
				}
			}
		}
}
JError
JExecute
	(
	const JPtrArray<JString>&	argList,
	pid_t*						childPID,
	const JExecuteAction		toAction,
	int*						toFD,
	const JExecuteAction		fromAction,
	int*						fromFD,
	const JExecuteAction		errAction,
	int*						errFD
	)
{
	const JSize argc = argList.GetElementCount();

	const JCharacter** argv = new const JCharacter* [ argc+1 ];
	assert( argv != NULL );

	for (JIndex i=1; i<=argc; i++)
		{
		argv[i-1] = *(argList.NthElement(i));
		}
	argv[argc] = NULL;

	const JError err = JExecute(argv, (argc+1) * sizeof(JCharacter*), childPID,
								toAction, toFD, fromAction, fromFD,
								errAction, errFD);

	delete [] argv;
	return err;
}
void
JXTreeListWidget::SelectNodes
	(
	const JPtrArray<JTreeNode>& list
	)
{
	JTableSelection& s = GetTableSelection();

	const JSize count = list.GetElementCount();
	for (JIndex i=1; i<=count; i++)
		{
		const JTreeNode* node = list.NthElement(i);
		JIndex index;
		if (itsTreeList->FindNode(node, &index))
			{
			const JPoint cell(itsNodeColIndex, index);
			s.SelectCell(cell);

			if (!s.HasAnchor())
				{
				s.SetAnchor(cell);
				}
			s.SetBoat(cell);
			}
		}
}
void
GenerateHeader
	(
	ostream&					output,
	const JPtrArray<JString>&	objTypes,
	const JPtrArray<JString>&	objNames
	)
{
	JIndex i;
	const JSize count = objTypes.GetElementCount();
	assert( count == objNames.GetElementCount() );

	// get width of longest type

	JSize maxLen = 0;
	for (i=1; i<=count; i++)
		{
		const JString* type = objTypes.NthElement(i);
		const JSize len     = type->GetLength();
		if (len > maxLen)
			{
			maxLen = len;
			}
		}

	// declare each object

	for (i=1; i<=count; i++)
		{
		output << "    ";
		const JString* type = objTypes.NthElement(i);
		type->Print(output);
		output << '*';
		const JSize len = type->GetLength();
		for (JIndex j=len+1; j<=maxLen+1; j++)
			{
			output << ' ';
			}
		(objNames.NthElement(i))->Print(output);
		output << ';' << endl;
		}

	// need blank line to conform to expectations of CopyAfterCodeDelimiter

	output << endl;
}
void
CBApp::CollectSearchPaths
	(
	CBDirInfoList* searchPaths
	)
	const
{
	searchPaths->DeleteAll();
	searchPaths->SetCompareFunction(CBDirInfo::ComparePathNames);

	JPtrArray<CBProjectDocument>* docList =
		(CBGetDocumentManager())->GetProjectDocList();

	const JSize docCount = docList->GetElementCount();
	JString truePath;
	JBoolean recurse;
	for (JIndex j=1; j<=docCount; j++)
		{
		CBProjectDocument* doc   = docList->NthElement(j);
		const CBDirList& dirList = doc->GetDirectories();
		const JSize count        = dirList.GetElementCount();
		for (JIndex i=1; i<=count; i++)
			{
			if (dirList.GetTruePath(i, &truePath, &recurse))
				{
				CBDirInfo newInfo(jnew JString(truePath), recurse);
				assert( newInfo.path != NULL );
				newInfo.projIndex = j;

				JBoolean found;
				const JIndex index =
					searchPaths->SearchSorted1(newInfo, JOrderedSetT::kAnyMatch, &found);
				if (found)
					{
					// compute OR of recurse flags

					CBDirInfo existingInfo = searchPaths->GetElement(index);
					if (newInfo.recurse && !existingInfo.recurse)
						{
						existingInfo.recurse = kJTrue;
						searchPaths->SetElement(index, existingInfo);
						}
					jdelete newInfo.path;
					}
				else
					{
					searchPaths->InsertElementAtIndex(index, newInfo);
					}
				}
			}
		}

	searchPaths->SetCompareFunction(CBDirInfo::CompareProjIndex);
	searchPaths->Sort();
}
CBStringCompleter::CBStringCompleter
	(
	const CBLanguage	lang,
	const JSize			keywordCount,
	const JCharacter**	keywordList,
	const JBoolean		caseSensitive
	)
	:
	itsLanguage(lang),
	itsPredefKeywordCount(keywordCount),
	itsPrefefKeywordList(keywordList),
	itsCaseSensitiveFlag(caseSensitive)
{
	itsStringList = new JPtrArray<JString>(JPtrArrayT::kForgetAll, 1000);
	assert( itsStringList != NULL );
	itsStringList->SetSortOrder(JOrderedSetT::kSortAscending);

	if (itsCaseSensitiveFlag)
		{
		itsStringList->SetCompareFunction(JCompareStringsCaseSensitive);
		}
	else
		{
		itsStringList->SetCompareFunction(JCompareStringsCaseInsensitive);
		}

	itsOwnedList = new JPtrArray<JString>(JPtrArrayT::kDeleteAll);
	assert( itsOwnedList != NULL );

	if (itsLanguage != kCBOtherLang)
		{
		ListenTo(CBGetDocumentManager());
		}

	CBGetStyler(lang, &itsStyler);
	UpdateWordList();
	if (itsStyler != NULL)
		{
		ListenTo(itsStyler);
		}

	// We are constructed at a random point in time, so we have to
	// ListenTo() all pre-existing project documents.

	JPtrArray<CBProjectDocument>* docList =
		(CBGetDocumentManager())->GetProjectDocList();

	const JSize docCount = docList->GetElementCount();
	for (JIndex i=1; i<=docCount; i++)
		{
		ListenTo(((docList->NthElement(i))->GetSymbolDirector())->GetSymbolList());
		}
}
void
GPrefsMgr::SetUIDList
	(
	const JPtrArray<JString>& list
	)
{
	const JSize count = list.GetElementCount();

	std::ostringstream data;
	data << count;

	for (JIndex i=1; i<=count; i++)
		{
		data << ' ' << *(list.NthElement(i));
		}

	SetData(kGUIDListID, data);
}
void
GPrefsMgr::SetInboxes
	(
	const JPtrArray<JString>& inboxes
	)
{
	const JSize count = inboxes.GetElementCount();

	std::ostringstream data;
	data << count;

	for (JIndex i=1; i<=count; i++)
		{
		data << ' ' << *(inboxes.NthElement(i));
		}

	SetData(kGInboxesID, data);
	Broadcast(InboxesChanged());
}
void
JXDocktab::UpdateActionMenu()
{
	JXDockManager* dockMgr;
	if (!JXGetDockManager(&dockMgr))
		{
		itsActionMenu->RemoveAllItems();
		return;
		}

	while (itsActionMenu->GetItemCount() >= kShowFirstDockCmd)
		{
		itsActionMenu->RemoveItem(itsActionMenu->GetItemCount());
		}

	JPtrArray<JXDockDirector>* dockList = dockMgr->GetDockList();

	const JSize dockCount = dockList->GetElementCount();
	for (JIndex i=1; i<=dockCount; i++)
		{
		JString itemText = ((dockList->NthElement(i))->GetWindow())->GetTitle();
		itemText.Prepend(JGetString(kShowDockPrefixID));
		itsActionMenu->AppendItem(itemText);
		}

	const JBoolean isDocked = (GetWindow())->IsDocked();
	itsActionMenu->SetItemEnable(kUndockCmd, isDocked);
	itsActionMenu->SetItemEnable(kUndockAllCompartmentCmd, isDocked);
	itsActionMenu->SetItemEnable(kUndockAllDockCmd, isDocked);
	itsActionMenu->SetItemEnable(kUndockAllCmd, JI2B(dockCount > 0));
	itsActionMenu->SetItemEnable(kDockAllDefConfigCmd, dockMgr->CanDockAll());

	itsActionMenu->SetItemEnable(kUpdateWindowTypeMapCmd, (GetWindow())->HasWindowType());
	if (isDocked)
		{
		itsActionMenu->SetItemText(kUpdateWindowTypeMapCmd, JGetString(kAddToWindowTypeMapID));
		}
	else
		{
		itsActionMenu->SetItemText(kUpdateWindowTypeMapCmd, JGetString(kRemoveFromWindowTypeMapID));
		}
}
JBoolean
ShouldGenerateForm
	(
	const JString&				form,
	const JPtrArray<JString>&	list
	)
{
	if (list.IsEmpty())
		{
		return kJTrue;
		}

	const JSize count = list.GetElementCount();
	for (JIndex i=1; i<=count; i++)
		{
		if (form == *(list.NthElement(i)))
			{
			return kJTrue;
			}
		}
	return kJFalse;
}
JBoolean
CBSymbolList::InContext
	(
	const JString&				fullName,
	const JPtrArray<JString>&	contextNamespace,
	const JBoolean				caseSensitive
	)
	const
{
	const JSize count = contextNamespace.GetElementCount();
	for (JIndex i=1; i<=count; i+=2)
		{
		const JString* cns1 = contextNamespace.NthElement(i);
		const JString* cns2 = contextNamespace.NthElement(i+1);
		if (fullName.BeginsWith(*cns1, caseSensitive) ||
			fullName.Contains(*cns2, caseSensitive))
			{
			return kJTrue;
			}
		}

	return kJFalse;
}
void
CBEditProjPrefsDialog::UpdateSettings()
{
	(CBGetApplication())->DisplayBusyCursor();

	const JBoolean reopenTextFiles      = itsReopenTextFilesCB->IsChecked();
	const JBoolean doubleSpaceCompile   = itsDoubleSpaceCB->IsChecked();
	const JBoolean rebuildMakefileDaily = itsRebuildMakefileDailyCB->IsChecked();

	const CBProjectTable::DropFileAction dropFileAction =
		(CBProjectTable::DropFileAction) itsDropFileActionRG->GetSelectedItem();

	CBDocumentManager* docMgr             = CBGetDocumentManager();
	JPtrArray<CBProjectDocument>* docList = docMgr->GetProjectDocList();
	const JSize docCount = docList->GetElementCount();

	for (JIndex i=1; i<=docCount; i++)
		{
		(docList->NthElement(i))->
			SetProjectPrefs(reopenTextFiles, doubleSpaceCompile,
							rebuildMakefileDaily, dropFileAction);
		}
}
void
JXFSBindingManager::Exec
(
    const JPtrArray<JString>&	fileList,
    const JBoolean				ignoreBindings
)
{
    if (fileList.IsEmpty())
    {
        return;
    }

    JXFSBindingManager* me = Instance();

    if (me->itsFileList == NULL)
    {
        me->itsFileList = new JPtrArray<JFSBinding>(JPtrArrayT::kDeleteAll);
        assert( me->itsFileList != NULL );
        me->itsFileList->SetCompareFunction(ComparePatterns);
    }

    const JSize count = fileList.GetElementCount();
    for (JIndex i=1; i<=count; i++)
    {
        const JString* fileName = fileList.NthElement(i);

        JFSBinding* f = new JFSBinding(*fileName, "", JFSBinding::kRunPlain, kJTrue, kJFalse);
        assert( f != NULL );
        if (!me->itsFileList->InsertSorted(f, kJFalse))
        {
            delete f;
        }
    }

    me->itsIgnoreBindingsFlag = ignoreBindings;
    me->ProcessFiles();
}
void
GetTempVarName
	(
	const JCharacter*			tagName,
	JString*					varName,
	const JPtrArray<JString>&	objNames
	)
{
	JString suffix = tagName;
	JIndexRange r;
	while (illegalCChar.Match(suffix, &r))
		{
		suffix.RemoveSubstring(r);
		}
	suffix.PrependCharacter('_');

	const JString prefix = "obj";
	const JSize count    = objNames.GetElementCount();
	for (JIndex i=1; i<=INT_MAX; i++)
		{
		*varName = prefix + JString(i) + suffix;
		JBoolean unique = kJTrue;
		for (JIndex j=1; j<=count; j++)
			{
			const JString* usedName = objNames.NthElement(j);
			if (*varName == *usedName)
				{
				unique = kJFalse;
				break;
				}
			}
		if (unique)
			{
			break;
			}
		}
}
Exemple #18
0
void
SMTPMessage::ReplaceAliases
	(
	JPtrArray<JString>& names
	)
{
	JPtrArray<JString> aliases(JPtrArrayT::kForgetAll);
	aliases.SetCompareFunction(JCompareStringsCaseSensitive);
	JSize i = 1;
	while (i <= names.GetElementCount())
		{
		JString& name = *(names.NthElement(i));
		JString alias;
		JString fcc;
		if (GGetAddressBookMgr()->NameIsAlias(name, alias, fcc))
			{
			JIndex findex;
			if (!aliases.SearchSorted(&name, JOrderedSetT::kAnyMatch, &findex))
				{
				GParseNameList(alias, names);
				aliases.InsertSorted(names.NthElement(i));
				names.RemoveElement(i);
				}
			else
				{
				names.DeleteElement(i);
				}
//			GParseNameList(fcc, names);
			}
		else
			{
			i++;
			}
		}
	aliases.DeleteAll();
}
JError
CBSearchDocument::Create
	(
	const JPtrArray<JString>&	fileList,
	const JPtrArray<JString>&	nameList,
	const JCharacter*			searchStr,
	const JBoolean				onlyListFiles,
	const JBoolean				listFilesWithoutMatch
	)
{
	assert( !fileList.IsEmpty() );
	assert( fileList.GetElementCount() == nameList.GetElementCount() );

	int fd[2];
	JError err = JCreatePipe(fd);
	if (!err.OK())
		{
		return err;
		}

	pid_t pid;
	err = JThisProcess::Fork(&pid);
	if (!err.OK())
		{
		return err;
		}

	// child

	else if (pid == 0)
		{
		close(fd[0]);

		// get rid of JXCreatePG, since we must not use X connection
		// (binary files may trigger it)
		JInitCore();

		CBSearchTE te;
		JOutPipeStream output(fd[1], kJTrue);
		te.SearchFiles(fileList, nameList,
					   onlyListFiles, listFilesWithoutMatch,
					   output);
		output.close();
		exit(0);
		}

	// parent

	else
		{
		close(fd[1]);

		JProcess* process = jnew JProcess(pid);
		assert( process != NULL );

		const JCharacter* map[] =
			{
			"s", searchStr
			};
		const JString windowTitle = JGetString("SearchTitle::CBSearchDocument", map, sizeof(map));

		CBSearchDocument* doc =
			jnew CBSearchDocument(kJFalse, JI2B(onlyListFiles || listFilesWithoutMatch),
								 fileList.GetElementCount(),
								 process, fd[0], windowTitle);
		assert( doc != NULL );
		doc->Activate();

		RecordLink* link;
		const JBoolean ok = doc->GetRecordLink(&link);
		assert( ok );
		CBSearchTE::SetProtocol(link);
		}

	return JNoError();
}
void
CBEditTextPrefsDialog::UpdateSettings()
{
	CBTextEditor* te = itsDoc->GetTextEditor();

	JString fontName;
	JSize fontSize;
	itsFontMenu->GetFont(&fontName, &fontSize);
	const JBoolean fontChanged = JI2B(
		fontName != te->GetDefaultFont().GetName() ||
		fontSize != te->GetDefaultFont().GetSize() );

	JFloat vScrollScale = 1.0;
	if (fontChanged)
		{
		const JFontManager* fontMgr = te->GetFontManager();
		const JFloat h1 = te->GetDefaultFont().GetLineHeight();
		const JFloat h2 = fontMgr->GetFont(fontName, fontSize).GetLineHeight();
		vScrollScale    = h2 / h1;
		}

	JInteger tabCharCount;
	JBoolean ok = itsTabCharCountInput->GetValue(&tabCharCount);
	assert( ok );

	JInteger crmLineWidth;
	ok = itsCRMLineWidthInput->GetValue(&crmLineWidth);
	assert( ok );

	JInteger undoDepth;
	ok = itsUndoDepthInput->GetValue(&undoDepth);
	assert( ok );

	JInteger rightMargin;
	ok = itsRightMarginInput->GetValue(&rightMargin);
	assert( ok );

	CBPrefsManager* prefsMgr = CBGetPrefsManager();
	const JBoolean textColorChanged = JNegate(
		itsColor[ CBPrefsManager::kTextColorIndex-1 ] ==
		prefsMgr->GetColor(CBPrefsManager::kTextColorIndex));

	// set colors before RecalcStyles() so stylers update themselves

	prefsMgr->SetDefaultFont(fontName, fontSize);
	for (JIndex j=1; j<=CBPrefsManager::kColorCount; j++)
		{
		prefsMgr->SetColor(j, itsColor[j-1]);
		}

	JPtrArray<CBTextDocument>* docList = (CBGetDocumentManager())->GetTextDocList();
	const JSize docCount = docList->GetElementCount();

	JProgressDisplay* pg = JNewPG();
	pg->FixedLengthProcessBeginning(docCount, "Updating preferences...", kJFalse, kJFalse);

	for (JIndex i=1; i<=docCount; i++)
		{
		CBTextDocument* doc = docList->NthElement(i);

		doc->ShouldMakeBackupFile(itsCreateBackupCB->IsChecked());
		doc->ShouldMakeNewBackupEveryOpen(!itsOnlyBackupIfNoneCB->IsChecked());
		doc->ShouldAllocateTitleSpace(itsExtraSpaceWindTitleCB->IsChecked());
		doc->ShouldOpenComplFileOnTop(itsOpenComplFileOnTopCB->IsChecked());

		te = doc->GetTextEditor();

		if (itsEmulatorIndex != itsOrigEmulatorIndex)
			{
			JTEKeyHandler* handler;
			CBInstallEmulator(kMenuIndexToEmulator[ itsEmulatorIndex-1 ], te, &handler);
			}

		te->ShouldAutoIndent(itsAutoIndentCB->IsChecked());
		te->CBShouldAllowDragAndDrop(itsUseDNDCB->IsChecked());
		te->ShouldMoveToFrontOfText(itsLeftToFrontOfTextCB->IsChecked());

		te->ShouldBalanceWhileTyping(itsBalanceWhileTypingCB->IsChecked());
		te->ShouldScrollToBalance(itsScrollToBalanceCB->IsChecked());
		te->ShouldBeepWhenTypeUnbalanced(itsBeepWhenTypeUnbalancedCB->IsChecked());

		te->TabShouldBeSmart(itsSmartTabCB->IsChecked());
		te->TabShouldInsertSpaces(itsTabToSpacesCB->IsChecked());

		if (fontChanged)
			{
			JXScrollbar *hScrollbar, *vScrollbar;
			const JBoolean ok = te->GetScrollbars(&hScrollbar, &vScrollbar);
			assert( ok );
			vScrollbar->PrepareForScaledMaxValue(vScrollScale);

			te->SetFont(fontName, fontSize, tabCharCount);
			}
		else
			{
			te->SetTabCharCount(tabCharCount);
			}

		te->SetCRMLineWidth(crmLineWidth);
		te->SetUndoDepth(undoDepth);
		te->SetRightMarginWidth(itsRightMarginCB->IsChecked(), rightMargin);

		te->SetDefaultFontStyle(itsColor [ CBPrefsManager::kTextColorIndex-1 ]);
		te->SetBackColor(itsColor [ CBPrefsManager::kBackColorIndex-1 ]);
		te->SetFocusColor(itsColor [ CBPrefsManager::kBackColorIndex-1 ]);
		te->SetCaretColor(itsColor [ CBPrefsManager::kCaretColorIndex-1 ]);
		te->SetSelectionColor(itsColor [ CBPrefsManager::kSelColorIndex-1 ]);
		te->SetSelectionOutlineColor(itsColor [ CBPrefsManager::kSelLineColorIndex-1 ]);
		te->SetRightMarginColor(itsColor [ CBPrefsManager::kRightMarginColorIndex-1 ]);

		if (textColorChanged)
			{
			te->RecalcStyles();
			}

		// force update of insertion font

		JIndex caretIndex;
		if (te->GetCaretLocation(&caretIndex))
			{
			te->SetCaretLocation(caretIndex);
			}

		pg->IncrementProgress();
		}

	CBFnMenuUpdater* updater = CBGetFnMenuUpdater();
	updater->ShouldSortFnNames(itsSortFnMenuCB->IsChecked());
	updater->ShouldIncludeNamespace(itsNSInFnMenuCB->IsChecked());
	updater->ShouldPackFnNames(itsPackFnMenuCB->IsChecked());

	JXTEBase::SetPartialWordModifier(
		(JXTEBase::PartialWordModifier) itsPWModRG->GetSelectedItem());

	JXTEBase::ShouldUseWindowsHomeEnd(itsHomeEndCB->IsChecked());
	CBTextEditor::CaretShouldFollowScroll(itsScrollCaretCB->IsChecked());
	JTextEditor::ShouldCopyWhenSelect(itsCopyWhenSelectCB->IsChecked());
	JXTEBase::MiddleButtonShouldPaste(itsMiddleButtonPasteCB->IsChecked());

	CBSearchTextDialog* dlog = CBGetSearchTextDialog();
	dlog->SetFont(fontName, fontSize);

	itsDoc->JPrefObject::WritePrefs();

	if (itsEmulatorIndex != itsOrigEmulatorIndex)
		{
		prefsMgr->SetEmulator(kMenuIndexToEmulator[ itsEmulatorIndex-1 ]);
		}

	CBMWriteSharedPrefs(kJTrue);

	pg->ProcessFinished();
	jdelete pg;
}
void
JXRadioGroupDialog::BuildWindow
	(
	const JCharacter*			windowTitle,
	const JCharacter*			prompt,
	const JPtrArray<JString>&	choiceList,
	const JPtrArray<JString>*	shortcutList
	)
{
JIndex i;

	const JSize actionCount = choiceList.GetElementCount();

	JXWindow* window = new JXWindow(this, 10,10, windowTitle);
	assert( window != NULL );

	JCoordinate y = kFirstItemTop;

	// instructions

	JXStaticText* instrText =
		new JXStaticText(prompt, window,
						 JXWidget::kFixedLeft, JXWidget::kFixedTop,
						 kHMarginWidth,y, 0,0);
	assert( instrText != NULL );

	y += instrText->GetFrameHeight() + kItemVDelta;

	// radio group

	const JCoordinate kInitRGWidth = 10;	// arbitrary, >0

	itsRG =
		new JXRadioGroup(window, JXWidget::kFixedLeft, JXWidget::kFixedTop,
						 kHMarginWidth,y,
						 kInitRGWidth, kItemVDelta + actionCount * kItemVSeparation);
	assert( itsRG != NULL );

	// choices

	JCoordinate wmin = 0;
	JPtrArray<JXRadioButton> buttonList(JPtrArrayT::kForgetAll, actionCount);
	for (i=1; i<=actionCount; i++)
		{
		JXTextRadioButton* button =
			new JXTextRadioButton(i, *(choiceList.NthElement(i)), itsRG,
								  JXWidget::kFixedLeft, JXWidget::kFixedTop,
								  kRGHLMarginWidth, kItemVDelta + (i-1) * kItemVSeparation,
								  10,kTextHeight);
		assert( button != NULL );

		if (shortcutList != NULL)
			{
			button->SetShortcuts(*(shortcutList->NthElement(i)));
			}

		buttonList.Append(button);
		wmin = JMax(button->GetPreferredWidth(), wmin);
		}

	// all choices should be the same width

	for (i=1; i<=actionCount; i++)
		{
		(buttonList.NthElement(i))->SetSize(wmin, kTextHeight);
		}

	wmin += kRGHLMarginWidth + kRGHRMarginWidth;
	itsRG->AdjustSize(wmin - kInitRGWidth, 0);

	const JCoordinate wminInstr = instrText->GetFrameWidth();
	if (wmin < wminInstr)
		{
		const JCoordinate delta = (wminInstr - wmin)/2;
		itsRG->Move(delta, 0);
		wmin = wminInstr;
		}

	y = (itsRG->GetFrame()).bottom + kRGButtonVDelta;

	// OK and Cancel buttons

	wmin += 2*kHMarginWidth;
	const JCoordinate wminButton = 3*kMinButtonHMargin + 2*kButtonWidth;
	if (wmin < wminButton)
		{
		const JCoordinate delta = (wminButton - wmin)/2;
		instrText->Move(delta, 0);
		itsRG->Move(delta, 0);
		wmin = wminButton;
		}

	const JCoordinate buttonX = (wmin - 2*kButtonWidth)/3;

	JXTextButton* cancelButton =
		new JXTextButton("Cancel", window,
						 JXWidget::kFixedLeft, JXWidget::kFixedTop,
						 buttonX,y, kButtonWidth,kTextHeight);
	assert( cancelButton != NULL );

	JXTextButton* okButton =
		new JXTextButton("OK", window,
						 JXWidget::kFixedLeft, JXWidget::kFixedTop,
						 wmin - buttonX - (kButtonWidth+2), y-1,
						 kButtonWidth+2, kTextHeight+2);
	assert( okButton != NULL );
	okButton->SetShortcuts("^M");

	SetButtons(okButton, cancelButton);

	// window size

	window->SetSize(wmin, y + kItemVSeparation);
}