/******************************************************************************
 JXSRTextBase.cc

	Full search & replace

	Derived classes must call inherited or SetAsActiveTextEditor() in
	the following functions:

	* HandleKeyPress
	* HandleShortcut
	* HandleMouseDown

	BASE CLASS = JXTEBase

	Copyright © 1998 by Glenn W. Bach. All rights reserved.

 ******************************************************************************/

#include "JXSRTextBase.h"
#include "JXSRTextDialogBase.h"

#include "jx_find.xpm"
#include "jx_find_previous.xpm"
#include "jx_find_next.xpm"
#include "jx_replace_selection.xpm"
#include "jx_find_selection_backwards.xpm"
#include "jx_find_selection_forward.xpm"

#include <JXDisplay.h>
#include <JXWindowDirector.h>
#include <JXWindow.h>
#include <JXMenuBar.h>
#include <JXTextMenu.h>
#include <JXFontManager.h>

#include <JString.h>
#include <JRegex.h>

#include <jXGlobals.h>

#include <jAssert.h>

// Search menu

static const JCharacter* kSearchMenuShortcutStr = "#S";

static const JCharacter* kSearchReplaceMenuTitleStr = "Search";
static const JCharacter* kMacSearchReplaceMenuStr =
	"Find... %k Meta-F %i \"Find::MSRSR\""
	"| Find previous %k Meta-Shift-G %i \"FindPrev::MSRSR\""
	"| Find next %k Meta-G %i \"FindNext::MSRSR\""
	"%l| Enter search string %k Meta-E  %i \"EnterSearch::MSRSR\""
	"| Enter replace string %k Meta-Shift-E %i \"EnterReplace::MSRSR\""
	"%l| Find selection backwards %k Meta-Shift-H  %i \"FindSelBack::MSRSR\""
	"| Find selection forward %k Meta-H %i \"FindSelFor::MSRSR\""
	"%l| Replace %k Meta-= %i \"Replace::MSRSR\""
	"| Replace and find previous %k Meta-Shift-L  %i \"ReplaceFindPrev::MSRSR\""
	"| Replace and find next %k Meta-L %i \"ReplaceFindNext::MSRSR\""
	"| Replace all forward | Replace all backward %i \"ReplaceAllFor::MSRSR\"";

static const JCharacter* kWinSearchReplaceMenuStr =
	"Find... %h f %k Ctrl-F  %i \"Find::WSRSR\""
	"| Find previous %h p %k Ctrl-Shift-G  %i \"FindPrev::WSRSR\""
	"| Find next %h n %k Ctrl-G %i \"FindNext::WSRSR\""
	"%l| Enter search string %h e %k Ctrl-E  %i \"EnterSearch::WSRSR\""
	"| Enter replace string %h r %k Ctrl-Shift-E %i \"EnterReplace::WSRSR\""
	"%l| Find selection backwards %h b %k Ctrl-Shift-H  %i \"FindSelBack::WSRSR\""
	"| Find selection forward %h o %k Ctrl-H %i \"FindSelFor::WSRSR\""
	"%l| Replace %k Ctrl-= %i \"Replace::WSRSR\""
	"| Replace and find previous %k Ctrl-Shift-L  %i \"ReplaceFindPrev::WSRSR\""
	"| Replace and find next %k Ctrl-L %i \"ReplaceFindNext::WSRSR\""
	"| Replace all forward | Replace all backward %i \"ReplaceAllFor::WSRSR\"";

static const JCharacter* kSearchMenuTitleStr = "Search";
static const JCharacter* kMacSearchMenuStr =
	"Find... %k Meta-F  %i \"Find::MSRS\""
	"| Find previous %k Meta-Shift-G %i \"FindPrev::MSRS\""
	"| Find next %k Meta-G %i \"FindNext::MSRS\""
	"%l| Enter search string %k Meta-E  %i \"EnterSearch::MSRS\""
	"%l| Find selection backwards %k Meta-Shift-H  %i \"FindSelBack::MSRS\""
	"| Find selection forward %k Meta-H %i \"FindSelFor::MSRS\"";

static const JCharacter* kWinSearchMenuStr =
	"Find... %h f %k Ctrl-F  %i \"Find::WSRS\""
	"| Find previous %h p %k Ctrl-Shift-G  %i \"FindPrev::WSRS\""
	"| Find next %h n %k Ctrl-G %i \"FindNext::WSRS\""
	"%l| Enter search string %h e %k Ctrl-E  %i \"EnterSearch::WSRS\""
	"%l| Find selection backwards %h b %k Ctrl-Shift-H  %i \"FindSelBack::WSRS\""
	"| Find selection forward %h o %k Ctrl-H %i \"FindSelFor::WSRS\"";


/******************************************************************************
 Constructor

 ******************************************************************************/

JXSRTextBase::JXSRTextBase
	(
	const Type 			type, 
	const JBoolean 		breakCROnly,
	const JBoolean 		pasteStyledText,
	JXScrollbarSet* 	scrollbarSet, 
	JXContainer* 		enclosure,
	const HSizingOption hSizing, 
	const VSizingOption vSizing,
	const JCoordinate 	x, 
	const JCoordinate 	y,
	const JCoordinate 	w, 
	const JCoordinate 	h
	)
	:
	JXTEBase(type, breakCROnly, pasteStyledText, scrollbarSet,
			 enclosure, hSizing, vSizing, x,y, w,h),
	itsSearchMenu(NULL),
	itsSearchReplaceMenu(NULL),
	itsSearchDialog(NULL)
{
}

/******************************************************************************
 Destructor

 ******************************************************************************/

JXSRTextBase::~JXSRTextBase()
{
}

/******************************************************************************
 Receive (virtual protected)

 ******************************************************************************/

void
JXSRTextBase::Receive
	(
	JBroadcaster*	sender,
	const Message&	message
	)
{
	const JBoolean hasFocus = HasFocus();

	if (sender == itsSearchMenu && message.Is(JXMenu::kNeedsUpdate) &&
		hasFocus)
		{
		UpdateSearchMenu();
		}
	else if (sender == itsSearchMenu && message.Is(JXMenu::kItemSelected) &&
			 hasFocus)
		{
		const JXMenu::ItemSelected* selection =
			dynamic_cast(const JXMenu::ItemSelected*, &message);
		assert( selection != NULL );
		HandleSearchMenu(selection->GetIndex());
		}
	else if (sender == itsSearchReplaceMenu &&
			 message.Is(JXMenu::kNeedsUpdate) &&
			 hasFocus)
		{
		UpdateSearchReplaceMenu();
		}
	else if (sender == itsSearchReplaceMenu &&
			 message.Is(JXMenu::kItemSelected) &&
			 hasFocus)
		{
		const JXMenu::ItemSelected* selection =
			dynamic_cast(const JXMenu::ItemSelected*, &message);
		assert( selection != NULL );
		HandleSearchReplaceMenu(selection->GetIndex());
		}

	else
		{
		JXTEBase::Receive(sender, message);
		}
}

/******************************************************************************
 SetAsActiveTextEditor (protected)

 ******************************************************************************/

void
JXSRTextBase::SetAsActiveTextEditor()
{
	if (itsSearchDialog != NULL)
		{
		itsSearchDialog->SetActiveTextEditor(this);
		}
}

/******************************************************************************
 UpdateSearchMenu (private)

 ******************************************************************************/

void
JXSRTextBase::UpdateSearchMenu()
{
	itsSearchMenu->EnableItem(kSFindCmd);

	if (itsSearchDialog->HasSearchString())
		{
		itsSearchMenu->EnableItem(kSFindNextCmd);
		itsSearchMenu->EnableItem(kSFindPrevCmd);

		}

	if (HasSelection())
		{
		itsSearchMenu->EnableItem(kSEnterSearchStrCmd);
		itsSearchMenu->EnableItem(kSFindSelectionFwdCmd);
		itsSearchMenu->EnableItem(kSFindSelectionBackCmd);
		}
}

/******************************************************************************
 HandleSearchMenu (private)

 ******************************************************************************/

void
JXSRTextBase::HandleSearchMenu
	(
	const JIndex index
	)
{
	Focus();
	SetAsActiveTextEditor();

	if (index == kSFindCmd)
		{
		itsSearchDialog->Activate();
		}
	else if (index == kSFindPrevCmd)
		{
		SRSearchBackward();
		}
	else if (index == kSFindNextCmd)
		{
		SRSearchForward();
		}
	else if (index == kSEnterSearchStrCmd)
		{
		EnterSearchSelection();
		}
	else if (index == kSFindSelectionBackCmd)
		{
		SearchSelectionBackward();
		}
	else if (index == kSFindSelectionFwdCmd)
		{
		SearchSelectionForward();
		}

}

/******************************************************************************
 UpdateSearchReplaceMenu (private)

 ******************************************************************************/

void
JXSRTextBase::UpdateSearchReplaceMenu()
{
	const JBoolean writable = JConvertToBoolean( GetType() == kFullEditor );

	itsSearchReplaceMenu->EnableItem(kSRFindCmd);

	if (itsSearchDialog->HasSearchString())
		{
		itsSearchReplaceMenu->EnableItem(kSRFindNextCmd);
		itsSearchReplaceMenu->EnableItem(kSRFindPrevCmd);

		if (writable)
			{
			itsSearchReplaceMenu->EnableItem(kSRReplaceAllForwardCmd);
			itsSearchReplaceMenu->EnableItem(kSRReplaceAllBackwardCmd);

			if (HasSelection())
				{
				itsSearchReplaceMenu->EnableItem(kSRReplaceCmd);
				itsSearchReplaceMenu->EnableItem(kSRReplaceAndFindNextCmd);
				itsSearchReplaceMenu->EnableItem(kSRReplaceAndFindPrevCmd);
				}
			}
		}

	if (HasSelection())
		{
		itsSearchReplaceMenu->EnableItem(kSREnterSearchStrCmd);
		itsSearchReplaceMenu->EnableItem(kSREnterReplaceStrCmd);
		itsSearchReplaceMenu->EnableItem(kSRFindSelectionFwdCmd);
		itsSearchReplaceMenu->EnableItem(kSRFindSelectionBackCmd);
		}
}

/******************************************************************************
 HandleSearchReplaceMenu (private)

 ******************************************************************************/

void
JXSRTextBase::HandleSearchReplaceMenu
	(
	const JIndex index
	)
{
	Focus();
	SetAsActiveTextEditor();

	if (index == kSRFindCmd)
		{
		itsSearchDialog->Activate();
		}
	else if (index == kSRFindPrevCmd)
		{
		SRSearchBackward();
		}
	else if (index == kSRFindNextCmd)
		{
		SRSearchForward();
		}
	else if (index == kSREnterSearchStrCmd)
		{
		EnterSearchSelection();
		}
	else if (index == kSREnterReplaceStrCmd)
		{
		EnterReplaceSelection();
		}
	else if (index == kSRFindSelectionBackCmd)
		{
		SearchSelectionBackward();
		}
	else if (index == kSRFindSelectionFwdCmd)
		{
		SearchSelectionForward();
		}

	else if (index == kSRReplaceCmd)
		{
		ReplaceSelection();
		}
	else if (index == kSRReplaceAndFindPrevCmd)
		{
		ReplaceAndSearchBackward();
		}
	else if (index == kSRReplaceAndFindNextCmd)
		{
		ReplaceAndSearchForward();
		}
	else if (index == kSRReplaceAllForwardCmd)
		{
		ReplaceAllForward();
		}
	else if (index == kSRReplaceAllBackwardCmd)
		{
		ReplaceAllBackward();
		}
}

/******************************************************************************
 HandleMouseDown (virtual protected)

 ******************************************************************************/

void
JXSRTextBase::HandleMouseDown
	(
	const JPoint&			pt,
	const JXMouseButton		button,
	const JSize				clickCount,
	const JXButtonStates&	buttonStates,
	const JXKeyModifiers&	modifiers
	)
{
	SetAsActiveTextEditor();
	JXTEBase::HandleMouseDown(pt, button, clickCount, buttonStates, modifiers);
}

/******************************************************************************
 HandleKeyPress (virtual)

 ******************************************************************************/

void
JXSRTextBase::HandleKeyPress
	(
	const int				key,
	const JXKeyModifiers&	modifiers
	)
{
	SetAsActiveTextEditor();
	JXTEBase::HandleKeyPress(key, modifiers);
}

/******************************************************************************
 HandleShortcut (virtual)

 ******************************************************************************/

void
JXSRTextBase::HandleShortcut
	(
	const int				key,
	const JXKeyModifiers&	modifiers
	)
{
	SetAsActiveTextEditor();
	JXTEBase::HandleShortcut(key, modifiers);
}

/******************************************************************************
 SRSearchForward

	Get the search parameters from the search dialog and look for the
	next match beyond the current position.  We beep if we can't find a match.

 ******************************************************************************/

JBoolean
JXSRTextBase::SRSearchForward()
{
	if (!itsSearchDialog->HasSearchString())
		{
		return kFalse;
		}

	JString searchStr;
	JBoolean searchIsRegex, caseSensitive, entireWord, wrapSearch;
	JBoolean preserveCase;
	JRegex* regex;
	if (!itsSearchDialog->GetSearchParameters(
			&searchStr, &searchIsRegex, &caseSensitive, &entireWord, &wrapSearch,
			&preserveCase, &regex))
		{
		return kFalse;
		}

	JBoolean found, wrapped;
	if (searchIsRegex)
		{
		(JXGetApplication())->DisplayBusyCursor();

		JArray<JIndexRange> submatchList;
		found = SearchForward(*regex, entireWord,
							  wrapSearch, &wrapped, &submatchList);
		}
	else
		{
		found = SearchForward(searchStr, caseSensitive, entireWord,
							  wrapSearch, &wrapped);
		}

	if (found)
		{
		TEScrollToSelection(kTrue);
		}

	if (!found || wrapped)
		{
		(GetDisplay())->Beep();
		}

	return found;
}

/******************************************************************************
 SRSearchBackward

	Get the search parameters from the search dialog and look for the
	match before the current position.  We beep if we can't find a match.

 ******************************************************************************/

JBoolean
JXSRTextBase::SRSearchBackward()
{
	if (!itsSearchDialog->HasSearchString())
		{
		return kFalse;
		}

	JString searchStr, replaceStr;
	JBoolean searchIsRegex, caseSensitive, entireWord, wrapSearch;
	JBoolean preserveCase;
	JRegex* regex;
	if (!itsSearchDialog->GetSearchParameters(
			&searchStr, &searchIsRegex, &caseSensitive, &entireWord, &wrapSearch,
			&preserveCase, &regex))
		{
		return kFalse;
		}

	JBoolean found, wrapped;
	if (searchIsRegex)
		{
		(JXGetApplication())->DisplayBusyCursor();

		JArray<JIndexRange> submatchList;
		found = SearchBackward(*regex, entireWord,
							   wrapSearch, &wrapped, &submatchList);
		}
	else
		{
		found = SearchBackward(searchStr, caseSensitive, entireWord,
							   wrapSearch, &wrapped);
		}

	if (found)
		{
		TEScrollToSelection(kTrue);
		}

	if (!found || wrapped)
		{
		(GetDisplay())->Beep();
		}

	return found;
}

/******************************************************************************
 EnterSearchSelection

	Copy the selection into the search field of the SearchTextDialog.
	Returns kTrue if there was something selected.

 ******************************************************************************/

JBoolean
JXSRTextBase::EnterSearchSelection()
{
	JString selText;
	if (GetSelection(&selText))
		{
		itsSearchDialog->SetSearchString(selText);
		return kTrue;
		}
	else
		{
		return kFalse;
		}
}

/******************************************************************************
 EnterReplaceSelection

	Copy the selection into the replace field of the SearchTextDialog.
	Returns kTrue if there was something selected.

 ******************************************************************************/

JBoolean
JXSRTextBase::EnterReplaceSelection()
{
	JString selText;
	if (GetSelection(&selText))
		{
		itsSearchDialog->SetReplaceString(selText);
		return kTrue;
		}
	else
		{
		return kFalse;
		}
}

/******************************************************************************
 ReplaceSelection

	Replace the current selection with the replace string from the
	SearchTextDialog.  Returns kFalse if nothing is selected or if the
	current selection doesn't match the search string.

 ******************************************************************************/

JBoolean
JXSRTextBase::ReplaceSelection()
{
	JString searchStr, replaceStr;
	JBoolean searchIsRegex, caseSensitive, entireWord, wrapSearch;
	JBoolean replaceIsRegex, preserveCase;
	JRegex* regex;
	if (!itsSearchDialog->GetSearchParameters(
			&searchStr, &searchIsRegex, &caseSensitive, &entireWord, &wrapSearch,
			&preserveCase, &regex))
		{
		return kFalse;
		}

	if (!itsSearchDialog->GetReplaceParameters(&replaceStr, &replaceIsRegex))
		{
		return kFalse;
		}

	JArray<JIndexRange> submatchList;
	if (searchIsRegex &&
		SelectionMatches(*regex, entireWord, &submatchList))
		{
		PrivateReplaceSelection(replaceStr, preserveCase,
								replaceIsRegex, *regex, submatchList);
		return kTrue;
		}
	else if (!searchIsRegex &&
			 SelectionMatches(searchStr, caseSensitive, entireWord))
		{
		submatchList.AppendElement(JIndexRange(1, searchStr.GetLength()));
		PrivateReplaceSelection(replaceStr, preserveCase,
								replaceIsRegex, *regex, submatchList);
		return kTrue;
		}

	(GetDisplay())->Beep();
	return kFalse;
}

/******************************************************************************
 PrivateReplaceSelection (private)

	Replace the current selection with the replace string from the
	SearchTextDialog.

	*** Something must be selected.
		We do not check that it matches the search string.

 ******************************************************************************/

void
JXSRTextBase::PrivateReplaceSelection
	(
	const JString&				replaceStr,
	const JBoolean				preserveCase,
	const JBoolean				replaceIsRegex,
	const JRegex&				regex,
	const JArray<JIndexRange>&	submatchList
	)
{
	assert( HasSelection() );

	JString replaceText;
	if (replaceIsRegex)
		{
		const JBoolean ok = GetSelection(&replaceText);
		assert( ok );
		replaceText = regex.InterpolateMatches(replaceText, submatchList);
		}
	else
		{
		replaceText = replaceStr;
		if (preserveCase)
			{
			JString sourceText;
			const JBoolean ok = GetSelection(&sourceText);
			assert( ok );
			replaceText.MatchCase(sourceText, submatchList.GetElement(1));
			}
		}

	const JIndex selStart = GetInsertionIndex();
	Paste(replaceText);
	if (!replaceText.IsEmpty())
		{
		SetSelection(selStart, selStart + replaceText.GetLength()-1);
		}
}

/******************************************************************************
 ReplaceAllForward

	Replace every occurrence of the search string with the replace string,
	starting from the current location.  Returns kTrue if it replaced anything.

 ******************************************************************************/

JBoolean
JXSRTextBase::ReplaceAllForward()
{
	if (!itsSearchDialog->HasSearchString())
		{
		return kFalse;
		}

	JString searchStr, replaceStr;
	JBoolean searchIsRegex, caseSensitive, entireWord, wrapSearch;
	JBoolean replaceIsRegex, preserveCase;
	JRegex* regex;
	if (!itsSearchDialog->GetSearchParameters(
			&searchStr, &searchIsRegex, &caseSensitive, &entireWord, &wrapSearch,
			&preserveCase, &regex))
		{
		return kFalse;
		}

	if (!itsSearchDialog->GetReplaceParameters(&replaceStr, &replaceIsRegex))
		{
		return kFalse;
		}

	(JXGetApplication())->DisplayBusyCursor();

	JArray<JIndexRange> submatchList;
	if (!searchIsRegex)
		{
		submatchList.AppendElement(JIndexRange());
		}

	if (wrapSearch)
		{
		SetCaretLocation(1);
		}
	else
		{
		SetCaretLocation(GetInsertionIndex());
		}

	JBoolean foundAny = kFalse;
	JBoolean found, wrapped;
	do
		{
		if (searchIsRegex)
			{
			found = SearchForward(*regex, entireWord,
								  kFalse, &wrapped, &submatchList);
			if (found)
				{
				AdjustRangesForReplace(&submatchList);
				}
			}
		else
			{
			found = SearchForward(searchStr, caseSensitive, entireWord,
								  kFalse, &wrapped);
			submatchList.SetElement(1, JIndexRange(1, searchStr.GetLength()));
			}

		if (found)
			{
			foundAny = kTrue;
			PrivateReplaceSelection(replaceStr, preserveCase,
									replaceIsRegex, *regex, submatchList);
			}
		}
		while (found);

	return foundAny;
}

/******************************************************************************
 ReplaceAllBackward

	Replace every occurrence of the search string with the replace string,
	starting from the current location.  Returns kTrue if it replaced anything.

 ******************************************************************************/

JBoolean
JXSRTextBase::ReplaceAllBackward()
{
	if (!itsSearchDialog->HasSearchString())
		{
		return kFalse;
		}

	JString searchStr, replaceStr;
	JBoolean searchIsRegex, caseSensitive, entireWord, wrapSearch;
	JBoolean replaceIsRegex, preserveCase;
	JRegex* regex;
	if (!itsSearchDialog->GetSearchParameters(
			&searchStr, &searchIsRegex, &caseSensitive, &entireWord, &wrapSearch,
			&preserveCase, &regex))
		{
		return kFalse;
		}

	if (!itsSearchDialog->GetReplaceParameters(&replaceStr, &replaceIsRegex))
		{
		return kFalse;
		}

	(JXGetApplication())->DisplayBusyCursor();

	JArray<JIndexRange> submatchList;
	if (!searchIsRegex)
		{
		submatchList.AppendElement(JIndexRange());
		}

	JIndex selStart, selEnd;
	if (wrapSearch)
		{
		SetCaretLocation(GetTextLength()+1);
		}
	else if (GetSelection(&selStart, &selEnd))
		{
		SetCaretLocation(selStart+1);
		}

	JBoolean foundAny = kFalse;
	JBoolean found, wrapped;
	do
		{
		if (searchIsRegex)
			{
			found = SearchBackward(*regex, entireWord,
								   kFalse, &wrapped, &submatchList);
			if (found)
				{
				AdjustRangesForReplace(&submatchList);
				}
			}
		else
			{
			found = SearchBackward(searchStr, caseSensitive, entireWord,
								   kFalse, &wrapped);
			submatchList.SetElement(1, JIndexRange(1, searchStr.GetLength()));
			}

		if (found)
			{
			foundAny = kTrue;
			PrivateReplaceSelection(replaceStr, preserveCase,
									replaceIsRegex, *regex, submatchList);
			}
		}
		while (found);

	return foundAny;
}

/******************************************************************************
 AdjustRangesForReplace (private)

	Shift all the ranges by the same amount so that the first one starts
	at index 1.

 ******************************************************************************/

void
JXSRTextBase::AdjustRangesForReplace
	(
	JArray<JIndexRange>* list
	)
{
	const JInteger delta = (list->GetElement(1)).first - 1;
	assert( delta >= 0 );

	if (delta > 0)
		{
		const JSize count = list->GetElementCount();
		for (JIndex i=1; i<=count; i++)
			{
			JIndexRange r = list->GetElement(i);
			r -= delta;
			list->SetElement(i, r);
			}
		}
}

/******************************************************************************
 AppendSearchMenu

	Call this to let us manage the Search menu for text searching.

 ******************************************************************************/

void
JXSRTextBase::AppendSearchMenu
	(
	JXSRTextDialogBase* dialog,
	JXMenuBar*			menuBar,
	const JXMenu::Style	menuStyle
	)
{
	assert( dialog				 != NULL );
	assert( itsSearchMenu 		 == NULL );
	assert( itsSearchReplaceMenu == NULL );

	itsSearchDialog = dialog;

	// create basic menu

	itsSearchMenuStyle   = menuStyle;

	itsSearchMenu = menuBar->AppendTextMenu(kSearchMenuTitleStr);
	if (itsSearchMenuStyle == JXMenu::kMacintoshStyle)
		{
		itsSearchMenu->SetMenuItems(kMacSearchMenuStr);
		}
	else
		{
		itsSearchMenu->SetShortcuts(kSearchMenuShortcutStr);
		itsSearchMenu->SetMenuItems(kWinSearchMenuStr);
		}

	itsSearchMenu->SetItemImage(kSFindCmd, jx_find);
	itsSearchMenu->SetItemImage(kSFindPrevCmd, jx_find_previous);
	itsSearchMenu->SetItemImage(kSFindNextCmd, jx_find_next);
	itsSearchMenu->SetItemImage(kSFindSelectionBackCmd, jx_find_selection_backwards);
	itsSearchMenu->SetItemImage(kSFindSelectionFwdCmd, jx_find_selection_forward);

	itsSearchMenu->SetUpdateAction(JXMenu::kDisableAll);
	ListenTo(itsSearchMenu);
}

/******************************************************************************
 ShareSearchMenu

	Call this to let us share the Search menu with other JXSRTextBase objects.

 ******************************************************************************/

void
JXSRTextBase::ShareSearchMenu
	(
	JXSRTextBase*		obj
	)
{
	assert( itsSearchMenu == NULL && obj->itsSearchMenu != NULL );

	itsSearchMenu              	= obj->itsSearchMenu;
	itsSearchMenuStyle         	= obj->itsSearchMenuStyle;
	itsSearchDialog				= obj->itsSearchDialog;
	ListenTo(itsSearchMenu);
}

/******************************************************************************
 AppendSearchReplaceMenu

	Call this to let us manage the Search menu for text editing.

 ******************************************************************************/

void
JXSRTextBase::AppendSearchReplaceMenu
	(
	JXSRTextDialogBase* dialog,
	JXMenuBar*			menuBar,
	const JXMenu::Style	menuStyle
	)
{
	assert( dialog				 != NULL );
	assert( itsSearchMenu 		 == NULL );
	assert( itsSearchReplaceMenu == NULL );
	assert(	dialog->CanReplace() 		 );

	itsSearchDialog = dialog;

	// create basic menu

	itsSearchReplaceMenuStyle = menuStyle;

	itsSearchReplaceMenu = menuBar->AppendTextMenu(kSearchReplaceMenuTitleStr);
	if (itsSearchReplaceMenuStyle == JXMenu::kMacintoshStyle)
		{
		itsSearchReplaceMenu->SetMenuItems(kMacSearchReplaceMenuStr);
		}
	else
		{
		itsSearchReplaceMenu->SetShortcuts(kSearchMenuShortcutStr);
		itsSearchReplaceMenu->SetMenuItems(kWinSearchReplaceMenuStr);
		}

	itsSearchReplaceMenu->SetItemImage(kSRFindCmd, jx_find);
	itsSearchReplaceMenu->SetItemImage(kSRFindPrevCmd, jx_find_previous);
	itsSearchReplaceMenu->SetItemImage(kSRFindNextCmd, jx_find_next);
	itsSearchReplaceMenu->SetItemImage(kSRReplaceCmd, jx_replace_selection);
	itsSearchReplaceMenu->SetItemImage(kSRFindSelectionBackCmd, jx_find_selection_backwards);
	itsSearchReplaceMenu->SetItemImage(kSRFindSelectionFwdCmd, jx_find_selection_forward);

	itsSearchReplaceMenu->SetUpdateAction(JXMenu::kDisableAll);
	ListenTo(itsSearchReplaceMenu);
}

/******************************************************************************
 ShareSearchReplaceMenu

	Call this to let us share the Search menu with other JXSRTextBase objects.

 ******************************************************************************/

void
JXSRTextBase::ShareSearchReplaceMenu
	(
	JXSRTextBase*		obj
	)
{
	assert( itsSearchReplaceMenu == NULL && obj->itsSearchReplaceMenu != NULL );

	itsSearchReplaceMenu			= obj->itsSearchReplaceMenu;
	itsSearchReplaceMenuStyle		= obj->itsSearchReplaceMenuStyle;
	itsSearchDialog					= obj->itsSearchDialog;
	ListenTo(itsSearchReplaceMenu);
}

/******************************************************************************
 SetFindDialogShortcut

	If the default shortcut to open the find dialog is not appropriate for
	your case, you can change it with this function. Simply pass in the new
	shortcut, ie:

	SetFindDialogShortcut("Meta-Shift-F");

	You need to call this after creating the Search or Search/Replace menu.
	
 ******************************************************************************/

void
JXSRTextBase::SetFindDialogShortcut
	(
	const JCharacter* str
	)
{
	assert(itsSearchMenu != NULL || itsSearchReplaceMenu != NULL);
	if (itsSearchMenu != NULL)
		{
		itsSearchMenu->SetItemNMShortcut(kSFindCmd, str);
		}
	else
		{
		itsSearchReplaceMenu->SetItemNMShortcut(kSRFindCmd, str);
		}
}