How could I open more than one image with a single dialog in Digital Micrograph script? - dm-script

I am writing in DigialMicrograph scripting. I want a script to open more than one image in a single dialog, a multi-select image dialog, similar to when you go to any Windows open dialog, select several images and press Ok.
I think that this is possible but I haven't found a way to do it. I want to introduce this specific code in my script, to avoid image-opening prior to running the script.
I know the function OpenDialog but this only allows opening of a single image. Could anybody show me some script or a function which can help me further?
Thanks.

What you are asking for might be a bit more involved. There is no multiple open function in the scripting language, so you can only create it yourself, involving multiple steps:
An input for a base-folder (This you could prompt the user for with a GetDirectoryDialog dialog)
Using the command GetFilesInDirectory to retrieve a list (TagList) of files in that folder
Process that list to filter out what you are interested in
Build a custom-dialog (derived from UIFrame) which lists the files with either checkboxes, multiple drop-down menus, or a list-box to select from.
Act on the selection made in your custom dialog, i.e. open all those files
The following script is an example doing that.
class MyMultiSelect : UIframe
{
string root
TagGroup FileList
TagGroup DLGlist
TagGroup CreateDLG( object self )
{
TagGroup dlg,dlgitems
dlg = DLGCreateDialog( "MultiOpen", dlgItems )
number nMax = fileList.TagGroupCountTags();
TagGroup listitems
DLGlist = DLGCreateList( listitems, 90, nMax+1 )
DLGlist.DLGMultipleSelect(1)
for (number i=0; i<nMax; i++)
{
string name
if ( FileList.TagGroupGetIndexedTagAsString(i,name) )
listitems.DLGAddListItem( name, 0 )
}
dlgitems.DLGAddElement(DLGlist)
return dlg
}
TagGroup CreateFilteredFileList( object self )
{
// Get all files
TagGroup list = GetFilesInDirectory( root, 1 )
// Filter all DM3 files
TagGroup filtered = NewTagList()
for( number i = 0; i<list.TagGroupCountTags(); i++)
{
TagGroup entry
string file = ""
list.TagGroupGetIndexedTagAsTagGroup(i,entry)
entry.TagGroupGetTagAsString( "Name", file )
if ( -1 != find(file,".dm3") )
filtered.TagGroupInsertTagAsString(Infinity(),file)
}
return filtered
}
TagGroup GetSelectedList( object self )
{
TagGroup selList = NewTagList()
TagGroup itemList
DLGlist.TagGroupGetTagAsTagGroup( "Items", itemList )
for ( number i=0; i<itemList.TagGroupCountTags(); i++ )
{
number isSelected = 0
TagGroup entry
itemList.TagGroupGetIndexedTagAsTagGroup(i,entry)
entry.TagGroupGetTagAsBoolean( "Selected", isSelected )
if ( isSelected )
{
string filename
entry.TagGroupGetTagAsString( "Label", fileName )
selList.TagGroupInsertTagAsString( Infinity(),fileName)
}
}
return selList
}
void OpenSelectedFiles( object self )
{
TagGroup files = self.GetSelectedList()
number nFiles = files.TagGroupCountTags()
if ( 0 == nFiles )
Throw( "No files selected" )
Result( "\n Opening "+nFiles+" files now..")
for ( number i=0; i<nFiles; i++ )
{
string filename
files.TagGroupGetIndexedTagAsString(i,fileName)
string path = root + "\\" + filename
Result( "\n Opening: "+path)
OpenImage(path).ShowImage()
}
}
Object Init( object self, string rootInput )
{
root = rootInput
FileList = self.CreateFilteredFileList( )
return self.super.Init( self.CreateDLG() )
}
}
// Main
{
string rootDir
GetDirectoryDialog( NULL, "Select Folder from which to multi-open", GetApplicationDirectory("current",0), rootDir )
object dlg = Alloc(MyMultiSelect).Init(rootDir)
dlg.pose()
dlg.OpenSelectedFiles()
}

Related

How to make a report parameter selection change another report parameter selection values while allowing multiselect for both of them?

Lets say I have a table with a list of names, such as "a, b, c" and each name has several other values assigned (some of the other values can be assigned to several/all of names values, example below).
Table example:
( names - other ):
a - aa
a - ab
a - ac
b - ab
b - bb
b - cb
c - ac
c - bc
c - cc
How do I make in birt so that I could select names in one parameter box and get corresponding other values to select for another parameter? I know it's possible to do that with cascading parameter, but that doesn't allow to have multiselect for the first parameter, which in our example would be names values.
Found a solution partly here: https://forums.opentext.com/forums/developer/discussion/61283/allow-multi-value-for-cascading-parameter
and here (thanks google translate ^^): https://www.developpez.net/forums/d1402270/logiciels/solutions-d-entreprise/business-intelligence/birt/birt-4-3-1-multi-value-cascade-parameters/
Steps to do:
change"\birt\webcontent\birt\ajax\ui\dialog\BirtParameterDialog.js" file contents (there is a file to download for replacement in one of the links, but in case it goes dead I'm sharing 2 snippets from it where the changes occur:
__refresh_cascade_select : function( element )
{
var matrix = new Array( );
var m = 0;
for( var i = 0; i < this.__cascadingParameter.length; i++ )
{
for( var j = 0; j < this.__cascadingParameter[i].length; j++ )
{
var paramName = this.__cascadingParameter[i][j].name;
if( paramName == this.__isnull )
paramName = this.__cascadingParameter[i][j].value;
if( paramName == element.id.substr( 0, element.id.length - 10 ) )
{
//CHANGE//
//The two way work (String(selectedValue) or Array(SelectedValueArray))
var l = 0;
var isFirstElement = true;
var selectedValue = "";
var selectedValueArray = new Array();
for(var test = 0; test<element.options.length;test++){
if(element.options[test].selected){
if(isFirstElement){
isFirstElement = false;
selectedValue = element.options[test].value;
}
else{
selectedValue = selectedValue+","+element.options[test].value;
}
selectedValueArray[l] = element.options[test].value;
l++;
}
}
//CHANGE//
var tempText = element.options[element.selectedIndex].text;
var tempValue = element.options[element.selectedIndex].value;
// Null Value Parameter
if ( tempValue == Constants.nullValue )
{
this.__cascadingParameter[i][j].name = this.__isnull;
this.__cascadingParameter[i][j].value = paramName;
}
else if( tempValue == '' )
{
if( tempText == "" )
{
var target = element;
target = target.parentNode;
var oInputs = target.getElementsByTagName( "input" );
if( oInputs.length >0 && oInputs[1].value != Constants.TYPE_STRING )
{
// Only String parameter allows blank value
alert( birtUtility.formatMessage( Constants.error.parameterNotAllowBlank, paramName ) );
this.__clearSubCascadingParameter( this.__cascadingParameter[i], j );
return;
}
else
{
// Blank Value
this.__cascadingParameter[i][j].name = paramName;
this.__cascadingParameter[i][j].value = tempValue;
}
}
else
{
// Blank Value
this.__cascadingParameter[i][j].name = paramName;
this.__cascadingParameter[i][j].value = tempValue;
}
}
else
{
this.__cascadingParameter[i][j].name = paramName;
//CHANGE//
//The two way work (String(selectedValue) or Array(SelectedValueArray))
this.__cascadingParameter[i][j].value = selectedValueArray;
//CHANGE//
}
for( var m = 0; m <= j; m++ )
{
if( !matrix[m] )
{
matrix[m] = {};
}
matrix[m].name = this.__cascadingParameter[i][m].name;
matrix[m].value = this.__cascadingParameter[i][m].value;
}
this.__pendingCascadingCalls++;
birtEventDispatcher.broadcastEvent( birtEvent.__E_CASCADING_PARAMETER, matrix );
}
}
}
},
and this:
// exist select control and input text/password
// compare the parent div offsetTop
if( oFirstITC.parentNode && oFirstST.parentNode )
{
// Bugzilla 265615: need to use cumulative offset for special cases
// where one element is inside a group container
var offsetITC = Position.cumulativeOffset( oFirstITC );
var offsetST = Position.cumulativeOffset( oFirstST );
// compare y-offset first, then x-offset to determine the visual order
if( ( offsetITC[1] > offsetST[1] ) || ( offsetITC[1] == offsetST[1] && offsetITC[0] > offsetST[0] ) )
{
oFirstST.focus( );
}
else
{
oFirstITC.focus( );
}
}
After .js is changed cascading parameters can have multiselect on all levels.
Example:
1st DataSet "DS_country" query:
select CLASSICMODELS.OFFICES.COUNTRY
from CLASSICMODELS.OFFICES
2nd DataSet "DS_office" query:
select CLASSICMODELS.OFFICES.OFFICECODE
from CLASSICMODELS.OFFICES
where CLASSICMODELS.OFFICES.COUNTRY IN ('DS_country')
After datasets are created we can make cascading report parameters CRP_country and CRP_office (keep in mind that UI doesn't let you to choose "Allow multiple values" on the upper levels, but we can change that in property editor after the parameters are made by going to advanced tab and changing "Scalar parameter type" property value to "Multi Value")
The only thing left is to pick lower level cascading parameters (CRP_office in our example) and go to script tab and add this on "beforeOpen":
// Do that if your selections are in String type.
var stringArray = params["CRP_country"].value.toString().split(",");
var result = "";
for(var i =0 ; i < stringArray.length ; i++){
if(i==0){
result = "'"+stringArray[i]+"'";
}
else{
result = result+",'"+stringArray[i]+"'";
}
}
// For String
this.queryText = this.queryText.replace("'DS_country'", result);
//For integer (the first part is useless)
//this.queryText = this.queryText.replace("'DS_country'",
params["CRP_country"].value);

How to disable/enable dialog elements

In this short dialog, I am trying to enable/disable the integer field. The DLGEnabled() command does not seem to do anything here:
class BTW_Dialog : UIFrame
{
BTW_Dialog(object self) { Result( "\n Object `" + self.ScriptObjectGetClassName() + "` ID:" + self.ScriptObjectGetID() + " created." ); }
~BTW_Dialog(object self) { Result( "\n Object `" + self.ScriptObjectGetClassName() + "` ID:" + self.ScriptObjectGetID() + " destroyed." ); }
TagGroup CreateDLGTagGroup( object self )
{
// Dialog building method
TagGroup DLGtgs, DLGItems
DLGtgs = DLGCreateDialog( "Analyze", DLGItems );
TagGroup RadioList = DLGCreateRadioList( 0, "AActOnRadio" )
RadioList.DLGAddRadioItem( "LP", 0 ).DLGIdentifier("0").DLGSide( "Left" );
RadioList.DLGAddRadioItem( "LF", 1 ).DLGIdentifier("1").DLGSide( "Left" );
DLGitems.DLGAddElement(RadioList).DLGAnchor("West");
TagGroup field = DLGCreateIntegerField( 55, 4 ).DLGSide( "Left" ).DLGIdentifier("xyz");
DLGitems.DLGAddElement(field).DLGAnchor("West");
return DLGtgs
}
object LaunchAsModelessDialog( object self )
{
self.init( self.CreateDLGTagGroup() );
self.Display( "Analyze" );
return self
}
void AActOnRadio( object self, tagGroup itemTG )
{
number radioButtonState = itemTG.DLGGetValue();
vtagGroup xyz_tag = self.LookupElement("xyz")
if(radioButtonState)
{ // trying to disable integer field: <<<-------||
DLGEnabled( xyz_tag, 0)
}
}
}
Alloc(BTW_Dialog).LaunchAsModeLessDialog();
Is there any other command to disable and/or hide the integer field when the radio button is pressed? Thanks.
The command you're looking for is
void SetElementIsEnabled( ScriptObject, String identifier, Boolean is_enabled )
i.e. in your example replace
DLGEnabled( xyz_tag, 0)
by
self.SetElementIsEnabled( "xyz", 0 )
Note, there is a similar command to make a dialog element "hidden", which is
void SetElementIsShown( ScriptObject, String identifier, Boolean is_shown )

How to sort an ObjectList in Digital Micrograph

Is it any possibilities to sort the ObjectList object?
For example:
class MissPlane: object
{
number d_local
object init(object self, number d)
{
d_local=d
return self
}
number getD(object self)
{
return d_local
}
void print(object self)
{
result("d="+d_local+"\n")
}
}
number h,k,l,hmax
hmax=2
result("start----------------"+datestamp()+"----------------------\n")
Object PlaneList
PlaneList=Alloc(ObjectList)
for(l=-hmax;l<=hmax;l++){
for(k=-hmax;k<=hmax;k++){
for(h=-hmax;h<=hmax;h++){
Object MPObject=Alloc(MissPlane)
MPObject.init(random())
PlaneList.AddObjectToList(MPObject)
MPObject.print()
}
}
}
And finally I need to sort it by d.
PS. ObjectList is not fully documented in the DM manual.
You can use the DM command sort to achieve sorting of either TagLists or ObjectList. In both cases you have to create a "sorting" class which defines a method comparing two elements and returning if the second element is bigger than the first.
The following example is for TagLists, sorting a numeric tagList to have increasing numbers.
Class Sorter
{
// The signature of the following method must not be changed.
Number Bigger( Object self, TagGroup TG1, Number index1, TagGroup TG2, Number index2 )
{
Number N1, N2
TG1.TagGroupGetIndexedTagAsNumber( index1, N1 )
TG2.TagGroupGetIndexedTagAsNumber( index2, N2 )
return (N1<N2)?1:0
}
}
TagGroup CreateRandomList( Number n )
{
TagGroup list = NewTagList()
For ( Number i = 0 ; i < n ; i++ )
list.TagGroupInsertTagAsNumber( i, Random() )
return list
}
void Main()
{
TagGroup unsorted = CreateRandomList( 10 )
unsorted.TagGroupOpenBrowserWindow( "unsorted", 0 )
Object sortObject = Alloc( Sorter )
Sort( 0, unsorted, sortObject, "Bigger" ) // The first parameter specifies ‘stable’ search
unsorted.TagGroupOpenBrowserWindow( "sorted", 0 )
}
Main()
The next example is for ObejctLists, sorting an objectList. In this case, objects of the list are simply "wrapped numbers" and the soring again achieves an increasing numbers.
class MyObject
{
Number value
Object Init( Object self, Number in ) { value = in; return self; }
Number Get( Object self ) { return value; }
}
Class Sorter
{
// The signature of the following method must not be changed.
Number HigherValue( Object self, Object obj1, Object obj2 )
{
return ( obj1.Get() < obj2.Get() ) ? 1 : 0
}
}
Object CreateRandomList( Number n )
{
Object obList = Alloc(ObjectList)
For ( Number i = 0 ; i < n ; i ++ )
{
Object newOBJ = Alloc(MyObject).Init( Random() )
obList.AddObjectToList( newOBJ )
}
return obList
}
void PrintObjects( Object obList )
{
ForEach( Object ob; obList )
Result( "value:" + ob.Get() + "\n" )
}
void Main()
{
Object unsorted = CreateRandomList( 7 )
Result( "\nBefore sort:\n" )
unsorted.PrintObjects()
Result( "\nAfter sort:\n" )
Sort( 0, unsorted, Alloc(Sorter), "HigherValue" )
unsorted.PrintObjects()
}
Main()
Both code examples have been found from this website.
The following example create a simple "bubble-sort" method to sort your object list:
class MissPlane: object
{
number d_local
object init(object self, number d)
{
d_local=d
return self
}
number getD(object self)
{
return d_local
}
void print(object self)
{
result("d="+d_local+"\n")
}
}
object sortList( object in )
{
object copy = in.ScriptObjectClone() // Making a copy avoids messing with your original list...
object sorted=Alloc(objectlist)
while( copy.SizeOfList() )
{
object t = copy.ObjectAt(0)
for (number c=1; c<copy.SizeOfList(); c++ )
{
if ( t.GetD() > copy.ObjectAt(c).GetD() )
t = copy.ObjectAt(c); // found a "smaller" object
}
sorted.AddObjectToList(t) // add the smallest item found
copy.RemoveObjectFromList(t)
}
return sorted
}
number h,hmax
hmax=20
result("start----------------"+datestamp()+"----------------------\n")
Object PlaneList
PlaneList=Alloc(ObjectList)
for(h=-0;h<hmax;h++){
Object MPObject=Alloc(MissPlane)
MPObject.init(random())
PlaneList.AddObjectToList(MPObject)
}
result("start---------------- ORIGINAL ----------------------\n")
ForEach( object o; PlaneList )
o.print()
Object sorted = sortList(PlaneList)
result("start---------------- SORTED ----------------------\n")
ForEach( object o; sorted)
o.print()

Defining a series of functions in DigitalMicrograph scripting

I have a set of functions inside a class that I need to define. Each passes a different value into another function:
void function00(object self, taggroup tg) self.otherfunction(tg,0,0)
void function01(object self, taggroup tg) self.otherfunction(tg,0,1)
void function02(object self, taggroup tg) self.otherfunction(tg,0,2)
void function03(object self, taggroup tg) self.otherfunction(tg,0,3)
void function04(object self, taggroup tg) self.otherfunction(tg,0,4)
I have 100 of these functions and I'd prefer not to define each one separately. Considering the above example I'd like to do something like:
for(number i=0; i<5; i++){
void function0+i(object self, taggroup tg) self.otherfunction(tg,0,i)
}
which doesn't work on it's own. Any suggestions?
For some more context I create a series of check boxes inside 2 for loops with the following:
BOXinsides.DLGAddElement(DLGCreateCheckBox(label,0,"function"+i+j).DLGIdentifier("#function"+i+j))
and I need to define all the functions in some sensible way.
DigitalMicrograph scripting does not allow this type of template code. However, you can solve your problem by linking all checkbox items to the same action-method. The signature of the action method passed in the TagGroup which is the checkbox item itself. You can use this to derive information from it, for example by looking at a checkbox property such as its title:
class myUI : UIframe
{
void generalFunction( object self , tagGroup checkTg )
{
// checkTg is the taggroup of the checkbox which fired the method.
// Use its Title to get back the running value!
string label = checkTg.DLGGetTitle()
Result( "\n label of checkbox:" + label )
number i = val( right( label, len( label ) - 1 ) )
Result( "\n running index:" + i )
}
TagGroup CreateCheckboxes( object self )
{
TagGroup checkboxGroup = DLGCreateGroup()
for ( number i = 0 ; I < 5 ; i++ )
{
checkboxGroup.DLGAddElement( DLGCreateCheckBox( "C" + I , 0 , "generalFunction" ) )
}
return checkboxGroup
}
TagGroup CreateDLGTags( object self )
{
TagGroup dlg, dlgitems
dlg = DLGCreateDialog( "Test" , dlgitems )
dlgitems.DLGAddElement( self.CreateCheckboxes() )
return dlg
}
object Init( object self )
{
return self.super.init( self.CreateDLGTags() )
}
}
// MAIN SCRIPT calling the dialog
{
Object dlg = Alloc(myUI).Init()
dlg.pose()
}
You can also 'attach' information directly to the checkbox. Checkboxes are - as all dialog items - really just specific TagGroup objects to which you can add whatever you like. In the example below, I'm adding an additional tag with a random number:
class myUI : UIframe
{
void generalFunction( object self , tagGroup checkTg )
{
// checkTg is the taggroup of the checkbox which fired the method.
// Use its Title to get back the running value!
string label = checkTg.DLGGetTitle()
Result( "\n label of checkbox:" + label )
number rnd
if ( checkTG.TagGroupGetTagAsNumber( "Random NR", rnd ) )
{
Result( "\n Random number:" + rnd )
}
}
TagGroup CreateCheckboxes( object self )
{
TagGroup checkboxGroup = DLGCreateGroup()
for ( number i = 0; I < 5 ; i++ )
{
TagGroup checkbox = DLGCreateCheckBox( "C" + I , 0 , "generalFunction" )
checkbox.TagGroupSetTagAsNumber( "Random NR", Random() )
checkboxGroup.DLGAddElement( checkbox )
}
return checkboxGroup
}
TagGroup CreateDLGTags( object self )
{
TagGroup dlg, dlgitems
dlg = DLGCreateDialog( "Test" , dlgitems )
dlgitems.DLGAddElement( self.CreateCheckboxes() )
return dlg
}
object Init( object self )
{
return self.super.init( self.CreateDLGTags() )
}
}
// MAIN SCRIPT calling the dialog
{
Object dlg=Alloc(myUI).Init()
dlg.pose()
}

StyledDocument adding extra count to indexof for each line of file

I have a strange problem (at least it appears that way) that when searching for a string in a textPane, I get an extra index for each line number that is searched and returned when using StyledDoc verses just getting the text from a textPane. I get the same text from the same pane, it's just that one is from the plain text the other is from the styled doc. Am I missing something here. I'll try to list as many of the changes between the two versions I am working with.
The plain text version:
public int displayXMLFile(String path, int target){
InputStreamReader inputStream;
FileInputStream fileStream;
BufferedReader buffReader;
if(target == 1){
try{
File file = new File(path);
fileStream = new FileInputStream(file);
inputStream = new InputStreamReader(fileStream,"UTF-8");
buffReader = new BufferedReader(inputStream);
StringBuffer content = new StringBuffer("");
String line = "";
while((line = buffReader.readLine())!=null){
content.append(line+"\n");
}
buffReader.close();
xhw.txtDisplay_1.setText(content.toString());
}
catch(Exception e){
e.printStackTrace();
return -1;
}
}
}
verses the Styled Doc (without the styles applied)
protected void openFile(String path, StyledDocument sDoc, int target)
throws BadLocationException {
FileInputStream fileStream;
String file;
if(target == 1){
file = "Openning First File";
} else {
file = "Openning Second File";
}
try {
fileStream = new FileInputStream(path);
// Get the object of DataInputStream
//DataInputStream in = new DataInputStream(fileStream);
ProgressMonitorInputStream in = new ProgressMonitorInputStream(
xw.getContentPane(), file, fileStream);
BufferedReader br = new BufferedReader(new InputStreamReader(in));
String strLine;
//Read File Line By Line
while ((strLine = br.readLine()) != null) {
sDoc.insertString(sDoc.getLength(), strLine + "\n", sDoc.getStyle("regular"));
xw.updateProgress(target);
}
//Close the input stream
in.close();
} catch (Exception e){//Catch exception if any
System.err.println("Error: " + e.getMessage());
}
This is how I search:
public int searchText(int sPos, int target) throws BadLocationException{
String search = xhw.textSearch.getText();
String contents;
JTextPane searchPane;
if(target == 1){
searchPane = xhw.txtDisplay_1;
} else {
searchPane = xhw.txtDisplay_2;
}
if(xhw.textSearch.getText().isEmpty()){
xhw.displayDialog("Nothing to search for");
highlight(searchPane, null, 0,0);
} else {
contents = searchPane.getText();
// Search for the desired string starting at cursor position
int newPos = contents.indexOf( search, sPos );
// cycle cursor to beginning of doc window
if (newPos == -1 && sPos > 0){
sPos = 0;
newPos = contents.indexOf( search, sPos );
}
if ( newPos >= 0 ) {
// Select occurrence if found
highlight(searchPane, contents, newPos, target);
sPos = newPos + search.length()+1;
} else {
xhw.displayDialog("\"" + search + "\"" + " was not found in File " + target);
}
}
return sPos;
}
The sample file:
<?xml version="1.0" encoding="UTF-8"?>
<AlternateDepartureRoutes>
<AlternateDepartureRoute>
<AdrName>BOIRR</AdrName>
<AdrRouteAlpha>..BROPH..</AdrRouteAlpha>
<TransitionFix>
<FixName>BROPH</FixName>
</TransitionFix>
</AlternateDepartureRoute>
<AlternateDepartureRoute>
</AlternateDepartureRoutes>
And my highlighter:
public void highlight(JTextPane tPane, String text, int position, int target) throws BadLocationException {
Highlighter highlighter = new DefaultHighlighter();
Highlighter.HighlightPainter painter = new DefaultHighlighter.DefaultHighlightPainter(Color.LIGHT_GRAY);
tPane.setHighlighter(highlighter);
String searchText = xhw.textSearch.getText();
String document = tPane.getText();
int startOfSString = document.indexOf(searchText,position);
if(startOfSString >= 0){
int endOfSString = startOfSString + searchText.length();
highlighter.addHighlight(startOfSString, endOfSString, painter);
tPane.setCaretPosition(endOfSString);
int caretPos = tPane.getCaretPosition();
javax.swing.text.Element root = tPane.getDocument().getDefaultRootElement();
int lineNum = root.getElementIndex(caretPos) +1;
if (target == 1){
xhw.txtLineNum1.setText(Integer.toString(lineNum));
} else if (target == 2){
xhw.txtLineNum2.setText(Integer.toString(lineNum));
} else {
xhw.txtLineNum1.setText(null);
xhw.txtLineNum2.setText(null);
}
} else {
highlighter.removeAllHighlights();
}
}
When I do a search for Alt with the indexof() I get 40 for the plain text (which is what it should return) and 41 when searching with the styled doc. And for each additional line that Alt appears on I get and extra index (so that the indexof() call returns 2 more then needed in line 3). This happens for every additional line that it finds. Am I missing something obvious? (If I need to push this to a smaller single class to make it easier to check I can do this later when I have some more time).
Thanks in advance...
If you are on Windows, then the TextComponent text (searchPane.getText()) can contain carriage-return+newline characters (\r\n), but the TextComponent's Styled Document (sSearchPane.getText(0, sSearchPane.getLength())) contains only newline characters (\n). That's why your newPos is always larger than newPosS by the number of newlines at that point. To fix this, in your search function you can change:
contents = searchPane.getText();
to:
contents = searchPane.getText().replaceAll("\r\n","\n");
That way the search occurs with the same indices that the Styled Document is using.
OK I have found a solution (basicly). I approached this from the aspect that I am getting text from the same text componet in two different ways...
String search = xw.textSearch.getText();
String contents;
String contentsS;
JTextPane searchPane;
StyledDocument sSearchPane;
searchPane = xw.txtDisplay_left;
sSearchPane = xw.txtDisplay_left.getStyledDocument();
contents = searchPane.getText();
contentsS = sSearchPane.getText(0, sSearchPane.getLength());
// Search for the desired string starting at cursor position
int newPos = contents.indexOf( search, sPos );
int newPosS = contentsS.indexOf(search, sPos);
So when comparing the two variables "newPos" & "newPosS", newPos retruned 1 more then newPosS for each line that the search string was found on. So when looking at the sample file and searching for "Alt" the first instance is found on line 2. "newPos" returns 41 and "newPosS returns 40 (which then highlights the correct text). The next occurance (which is found in line 3) "newPos" returns 71 and "newPosS" returns 69. As you can see, every new line increases the count by the line number the occurance begins in. I would suspect that there is an extra character being added in for each new line from the textPane that is not present in the StyledDoc.
I'm sure there is a reasonable explaination but I don't have it at this time.