package Schmortopf.JavaSourceEditor.CodeCompletion;
/**
* Created and used by EditorPanel.performCodeCompletion() .
*
*
* This one extends from JPopupMenu and has the GUI included therefore.
*
* An instance of this class is created in the method
* EditorPanel.performCodeCompletion().
*
* All calls are made OUTSIDE the eventdispatchthread, so this object
* must switch into the eventdispatch thread (or put tasks in its queue)
* when it performs Swing tasks.
*
* It tries to translate the passed basisIdentifier
* into a valid class identifier, shows its reachable member
* attributes and methods in a popup and inserts the selected
* name into the passed insertPosition of the current SourceEditorDocument.
*
*/
import java.util.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.text.*;
import javax.swing.undo.*;
import javax.swing.event.*;
import javax.swing.border.*;
import javax.swing.tree.*;
import javax.swing.plaf.metal.*;
import Schmortopf.Main.IDE_ProjectFrameProvider;
import Schmortopf.FileStructure.*;
import Schmortopf.FileStructure.Descriptions.*;
import Schmortopf.JavaSourceEditor.*;
import Schmortopf.JavaSourceEditor.DocumentStyle.*;
import Schmortopf.Utility.gui.*;
import Schmortopf.Utility.IniFile.IniFile;
import Schmortopf.Utility.ThreadEngine.ThreadEngine;
import Schmortopf.FileComponents.View.SelfDrawingIcon;
import Schmortopf.ProjectFiles.SelectionTree.SelectionTreeIcon;
import Schmortopf.ProjectFiles.SelectionTree.SelectionTreeObject;
import Language.Language;
import Shared.Logging.Log;
public class CodeCompletion extends JResizableUndecoratedFrame
{
private final static String JavaCheckBoxSelectionIniFileKey = "CodeCompletion_JavaCheckBoxSelectionIniFileKey";
private final static String RememberWindowSizeIniFileKey = "CodeCompletion_RememberWindowSizeIniFileKey";
private final static String DoShowOnlyMatchesIniFileKey = "CodeCompletion_DoShowOnlyMatches";
private final static String WindowWidthIniFileKey = "CodeCompletion_WindowWidthIniFileKey";
private final static String WindowHeightIniFileKey = "CodeCompletion_WindowHeightIniFileKey";
private final static String WindowSplitLocationIniFileKey = "CodeCompletion_WindowSplitLocationIniFileKey";
private static final SelfDrawingIcon OnIcon =
new SelfDrawingIcon( Language.Translate("on") ,SelfDrawingIcon.ColorGreen);
private static final SelfDrawingIcon OffIcon =
new SelfDrawingIcon( Language.Translate("off") ,SelfDrawingIcon.ColorRed );
private static final SelectionTreeIcon SelectedIcon =
new SelectionTreeIcon( SelectionTreeObject.StateIsSelected,false,
SelectionTreeObject.Accessibility_Neutral );
private static final SelectionTreeIcon NotSelectedIcon =
new SelectionTreeIcon( SelectionTreeObject.StateIsHidden,false,
SelectionTreeObject.Accessibility_Neutral );
private static final SelfDrawingIcon SearchIcon =
new SelfDrawingIcon( Language.Translate("Search") ,SelfDrawingIcon.ColorBlue);
private static final SelfDrawingIcon JumpIcon =
new SelfDrawingIcon( Language.Translate("Jump") ,SelfDrawingIcon.ColorGreen );
private EditorPanel editorPanel;
private FileStructureDescription currentlyEditedFSD;
private FileStructureDescriptionManager fileStructureDescriptionManager;
private IDE_ProjectFrameProvider projectFrame;
private IniFile iniFile;
private final String basisIdentifier;
private final int insertPosition;
final Point insertionPoint; // in the editorPanel coordsystem
private CodeCompletion thisCodeCompletion;
private JPanel splitWrapPanel;
private JSplitPane splitPane;
private JPanel mainPanel;
private int originalDividerSize = 10;
private JList entryList = null; // Contains the cc entries
private boolean terminateHasBeenCalled = false;
private boolean doShowOnlyMatches = false; // Used in the method/attribute cc window
// Loaded from the inifile
// Tracks user inputs into the editor while this window is displayed.
private final StringBuffer stringBuffer = new StringBuffer();
/**
* Create the CodeCompletion popup with entries for the
* passed basisIdentifier (if one can translate it into a valid
* class identifier). The selection then is inserted at the passed
* insertPosition.
*
* Note: The constructor is designed to be called from OUTSIDE
* the eventdispatch thread.
*/
public CodeCompletion( final String basisIdentifier,
final int insertPosition,
final Point insertionPoint,
final EditorPanel editorPanel,
final FileStructureDescription currentlyEditedFSD,
final FileStructureDescriptionManager fileStructureDescriptionManager,
final IDE_ProjectFrameProvider projectFrame,
final IniFile iniFile )
{
super();
// String title, boolean resizable, boolean closable,
// boolean maximizable, boolean iconifiable
this.basisIdentifier = basisIdentifier;
this.insertPosition = insertPosition;
this.insertionPoint = insertionPoint;
this.editorPanel = editorPanel;
this.currentlyEditedFSD = currentlyEditedFSD;
this.fileStructureDescriptionManager = fileStructureDescriptionManager;
this.projectFrame = projectFrame;
this.iniFile = iniFile;
this.thisCodeCompletion = this;
this.doShowOnlyMatches = this.iniFile.getBooleanValue(DoShowOnlyMatchesIniFileKey,false);
} // Constructor
/**
* The only public method, which performs the search using the
* parameters passed to the constructor.
*
* The triggerCharacter = "." or "(" also is passed to this method. If it's
* a point or a space, either the method/attribute or the import CodeCompletion
* starts up, otherwise
* if its an opening bracket, the parameter CodeCompletion starts up.
*
* Note: This is called from OUTSIDE the eventdispatch thread.
*/
public void performFSDSearch( final char triggerCharacter,
final int caretLineNumber )
{
//Log.Info("called for basisIdentifier = " +
// this.basisIdentifier + " with length= " + this.basisIdentifier.length() +
// " and trigger=" + triggerCharacter );
//long startTime = System.currentTimeMillis();
if( this.currentlyEditedFSD == null )
{
Log.Error("Designerror: currentlyEditedFSD is null. Returning.");
return;
}
if( this.basisIdentifier.length() > 0 )
{
// Choose the CC mode depending on the trigger character :
if( ( triggerCharacter == '.' ) || // [ point or space]
( triggerCharacter == ' ' ) ) // -> start the method/attributes or import CC :
{
//ystem.out.println("CodeCompletion.performFSDSearch() started for trigger = space or point");
// Conditions for one of the two possibilities :
//
// First we get the linenumber of the class declaration.
// (= the line containing public class myClass extends .. or so )
// an compare it with the current linenumber of the caret ( = the cursor).
//
// Case A )
// The cursor is ABOVE the class declaration line, OR
// a class declaration doesn't exist yet in the editedFSD, cause
// the programmer has not written one yet.
// -> In this case, only the import CC can make sense.
//
// Case B )
// The class declaration line DOES exist in the edited FSD AND
// the cursor is on the class declaration line or below.
// -> start the method/attribute CC.
//
// Do that :
final int classDeclarationLine = this.currentlyEditedFSD.className.startLine;
//ystem.out.println("CodeCompletion.performFSDSearch(): classDeclarationLine= " + classDeclarationLine +
// " and caretLineNumber= " + caretLineNumber);
if( ( classDeclarationLine == -1 ) || // -> doesn't exist yet
( caretLineNumber < classDeclarationLine ) )
{
// Import CC
// See, if the line contains a static import statement so far:
String lineText = this.editorPanel.getEditor().getLineWithNumber(caretLineNumber);
// 1.5 static import extension recognition:
boolean isStaticImport = (lineText.indexOf(" static") >= 0 );
// import CC :
final CodeCompletionImportSearch codeCompletionImportSearch =
new CodeCompletionImportSearch( triggerCharacter,
isStaticImport,
this.basisIdentifier,
this.insertPosition,
this.editorPanel,
this.currentlyEditedFSD,
this.fileStructureDescriptionManager );
final StringListEntry[] entryArray = codeCompletionImportSearch.performSearch(caretLineNumber);
if( entryArray != null )
{
if( entryArray.length > 0 )
{
// Show the method/attribute CodeCompletion popup :
this.showImportCodeCompletionPopup(entryArray);
}
}
}
else
{
// Method/attribute CC :
final CodeCompletionSearch codeCompletionSearch =
new CodeCompletionSearch( this.basisIdentifier,
this.insertPosition,
this.editorPanel,
this.currentlyEditedFSD,
this.fileStructureDescriptionManager );
final CodeCompletionListEntry[] codeCompletionListEntryArray =
codeCompletionSearch.performSearch( caretLineNumber );
if( codeCompletionListEntryArray != null )
{
if( codeCompletionListEntryArray.length > 0 )
{
// Show the method/attribute CodeCompletion popup.
// Use the classname of the class associated with the found fsd as title :
this.showCodeCompletionPopup( codeCompletionListEntryArray,
codeCompletionSearch.getFoundIdentifierName() );
}
}
// We **must** release any updated transient fields now:
codeCompletionSearch.freeAllTransientFields();
} // else
}
else // it should be a "(" sign -> parameter CC :
{
//ystem.out.println("CodeCompletion.performFSDSearch() started for trigger = (");
final CodeCompletionParameterSearch codeCompletionParameterSearch =
new CodeCompletionParameterSearch( this.basisIdentifier,
this.insertPosition,
this.editorPanel,
this.currentlyEditedFSD,
this.fileStructureDescriptionManager );
final CodeCompletionListEntry[] codeCompletionListEntryArray = codeCompletionParameterSearch.performSearch(caretLineNumber);
if( codeCompletionListEntryArray != null )
{
if( codeCompletionListEntryArray.length > 0 )
{
// Show the Parameter CodeCompletion popup :
this.showParameterCodeCompletionPopup( codeCompletionListEntryArray,
codeCompletionParameterSearch.getFoundIdentifierName() );
}
}
}
} // if
//long elapsedTime = System.currentTimeMillis() - startTime;
//Log.Info("CodeCompletion.performFSDSearch() ends. Elapsed time [ms] = " +
// elapsedTime );
} // performFSDSearch
/**
* Shows the method/attribute CodeCompletion popup. Swing-Thread-Safe.
*/
private void showCodeCompletionPopup( final CodeCompletionListEntry[] codeCompletionListEntryArray,
final String popupTitle )
{
// Only show it, if we have some items to display at all:
if( codeCompletionListEntryArray.length > 0 )
{
final CodeCompletionListModel listModel =
new CodeCompletionListModel( codeCompletionListEntryArray,
this.fileStructureDescriptionManager );
listModel.setShowOnlyMatches( this.doShowOnlyMatches ); // Set the display mode
this.entryList = new JList(listModel);
this.entryList.setCellRenderer( new CodeCompletionListCellRenderer() );
// Keypress reaction: forward letters and numbers,
// close on return or other keys :
this.entryList.addKeyListener( new KeyAdapter()
{
public void keyTyped(KeyEvent e)
{
final char ch = e.getKeyChar();
final int charValue = (int)ch;
//ystem.out.println(" ch = " + ch );
//ystem.out.println(" charValue = " + charValue );
//ystem.out.println(" keycode = " + e.getKeyCode() );
// Temporary: ctrl and alt keys are not handled for now.
// The problem additionally is, that the fsdupdater starts
// and produces a null fsd, if we process such events here.
// Producing a null fsd can have serious effects for the
// project and dependency calculations.
// Also the keycode only is available in keyPressed or
// keyReleased methods [gives some troubles & requires win+linux tests]
if( e.isAltDown() || e.isControlDown() )
{
//ystem.out.println("modifier ctrl(alt down");
return;
}
if( charValue == 8 ) // backspace was pressed
{
if( stringBuffer.length() > 0 )
{
try
{
// remove the old stringbuffer from the editor :
editorPanel.getEditor().getDocument().remove(insertPosition,stringBuffer.length() );
// update the stringBuffer and the popup selection :
stringBuffer.setLength( stringBuffer.length()-1 );
selectBestMatchingEntry( entryList,listModel,stringBuffer.toString() );
// insert the new stringbuffer content :
final Style style = StylesFactory.GetEditorTextStyle();
editorPanel.getEditor().getDocument().insertString(insertPosition,stringBuffer.toString(),style );
}
catch( Exception badloc3454 )
{
badloc3454.printStackTrace();
}
}
else
{
// Remove the point character :
try
{
editorPanel.getEditor().getDocument().remove(insertPosition-1,1 );
}
catch( Exception e234876 ){}
// and just close the CC view :
if( ( isVisible() ) && (thisCodeCompletion != null ) )
{
setVisible(false);
terminate();
}
} // else
}
else // if Return (CR of LF) was pressed, append the current selection and close it :
if( ( charValue == 13 ) || ( charValue == 10 ) )
{
try
{
// First remove the current buffer content, if any is there :
if( stringBuffer.length() > 0 )
{
// remove the old stringbuffer from the editor :
editorPanel.getEditor().getDocument().remove(insertPosition,stringBuffer.length() );
stringBuffer.setLength(0);
}
// Then take the current selection and append this one :
final int selectedIndex = entryList.getSelectedIndex();
if( selectedIndex >= 0 )
{
final CodeCompletionListEntry selectedEntry = (CodeCompletionListEntry)entryList.getModel().getElementAt( selectedIndex );
// and run it :
selectedEntry.run();
}
}
catch( Exception badloc6354 )
{
badloc6354.printStackTrace();
}
// And close it :
if( ( isVisible() ) && (thisCodeCompletion != null ) )
{
setVisible(false);
terminate();
}
}
else // Escape was pressed: Close it :
if( charValue == 27 )
{
// just close it :
if( ( isVisible() ) && (thisCodeCompletion != null ) )
{
setVisible(false);
terminate();
}
}
else
{
if( stringBuffer.length() < 1024 )
{
// If the typed character has not been a java-identifier-character,
// close the popup :
if( Character.isJavaIdentifierPart(ch) )
{
try
{
// update the stringBuffer and the popup selection :
stringBuffer.append(ch);
selectBestMatchingEntry( entryList,listModel,stringBuffer.toString() );
// insert the new character into the document too :
final Style style = StylesFactory.GetEditorTextStyle();
editorPanel.getEditor().getDocument().insertString(insertPosition + stringBuffer.length()-1,""+ch,style );
}
catch( Exception badloc12376 )
{
badloc12376.printStackTrace();
}
}
else
{
// If the typed character has not been a java-identifier-character,
// close the popup :
if( ( isVisible() ) && (thisCodeCompletion != null ) )
{
setVisible(false); // clear the way for a possible follower cc
try
{
// update the stringBuffer and the popup selection :
stringBuffer.append(ch);
// insert the new character into the document too :
final Style style = StylesFactory.GetEditorTextStyle();
editorPanel.getEditor().getDocument().insertString(insertPosition + stringBuffer.length()-1,""+ch,style );
}
catch( Exception badloc12376 )
{
badloc12376.printStackTrace();
}
terminate();
}
}
}
}
} // keyTyped
}); // KeyAdapter
// Select and close on double clicks :
this.entryList.addMouseListener( new MouseAdapter()
{
public void mouseClicked( MouseEvent e )
{
if( e.getClickCount() > 1 ) // double click required
{
final int selectedIndex = entryList.getSelectedIndex();
if( selectedIndex >= 0 )
{
final CodeCompletionListEntry selectedEntry = (CodeCompletionListEntry)entryList.getModel().getElementAt( selectedIndex );
selectedEntry.run();
// Remove all - give the GC a better chance :
if( ( isVisible() ) && (thisCodeCompletion != null ) )
{
setVisible(false);
terminate();
}
}
}
}
});
// close it, when it looses the focus :
this.entryList.addFocusListener( new FocusAdapter()
{
public void focusLost( FocusEvent e )
{
// Remove all - give the GC a better chance :
if( ( isVisible() ) && (thisCodeCompletion != null ) )
{
setVisible(false);
terminate();
}
}
});
// The list is complete including attached listeners, now show it.
// JavaDoc available -> last parm set.
this.showPopup( this.entryList,listModel,popupTitle,true,true,true );
} // if
} // showCodeCompletionPopup
private void selectBestMatchingEntry( final JList list,
final CodeCompletionListModel listModel,
final String compareBasis )
{
// Update the ccText always. It wouldn't be required when doShowOnlyMatches is clear,
// but it's required when the user changes between the two modes for having
// immediate update, so update it always:
listModel.setCurrentccText( compareBasis );
// update the model, which has an effect only, if its memberattribute
// doShowOnlyMatches is set:
if( listModel.getShowOnlyMatches() )
{
listModel.rebuildList();
}
if( compareBasis.length() > 0 )
{
int indexForSelection = 0;
while( indexForSelection < listModel.getSize() )
{
final CodeCompletionListEntry selectedEntry = (CodeCompletionListEntry)listModel.getElementAt( indexForSelection );
final String compareString = selectedEntry.getComparatorString();
if( compareBasis.compareToIgnoreCase(compareString) <= 0 )
{
break; // found the best matching entry
}
else
{
indexForSelection++;
}
}
// select and scroll to the selected line :
selectAndScrollToListIndex( list, indexForSelection );
}
} // selectBestMatchingEntry
/**
* Helps the GC be releasing associations.
*/
private void terminate()
{
if( !this.terminateHasBeenCalled )
{
this.terminateHasBeenCalled = true;
// This CodeCompletion object runs in a ThreadEngine thread,
// so we are safe to free the member attributes, if we delay
// it in a ThreadEngine Thread :
Runnable terminatorRunnable = new Runnable()
{
public void run()
{
EventQueue.invokeLater( new Runnable()
{
public void run()
{
internal_delayed_terminate();
}
});
}
};
ThreadEngine.getInstance().addRunnable(terminatorRunnable,"CC TerminatorRunnable");
}
} // terminate
private void internal_delayed_terminate()
{
getContentPane().removeAll();
thisCodeCompletion = null;
editorPanel = null;
currentlyEditedFSD = null;
fileStructureDescriptionManager = null;
projectFrame = null;
iniFile = null;
splitWrapPanel = null;
splitPane = null;
mainPanel = null;
this.dispose(); // Linux requirement: Kill it completely.
} // internal_terminate
/**
* Shows the Import CodeCompletion popup. Swing-Thread-Safe.
*/
private void showImportCodeCompletionPopup( final StringListEntry[] stringListEntryArray )
{
// Only show it, if we have some items to display at all:
if( stringListEntryArray.length > 0 )
{
final StringListModel listModel = new StringListModel(stringListEntryArray);
this.entryList = new JList(listModel);
this.entryList.setCellRenderer( new StringListCellRenderer() );
// Keypress reaction: forward letters and numbers,
// close on return or other keys :
this.entryList.addKeyListener( new KeyAdapter()
{
public void keyTyped(KeyEvent e)
{
final char ch = e.getKeyChar();
final int charValue = (int)ch;
if( charValue == 8 ) // backspace was pressed
{
if( stringBuffer.length() > 0 )
{
try
{
// remove the old stringbuffer from the editor :
editorPanel.getEditor().getDocument().remove(insertPosition,stringBuffer.length() );
// update the stringBuffer and the popup selection :
stringBuffer.setLength( stringBuffer.length()-1 );
local_selectBestMatchingEntry();
// insert the new stringbuffer content :
final Style style = StylesFactory.GetEditorTextStyle();
editorPanel.getEditor().getDocument().insertString(insertPosition,stringBuffer.toString(),style );
}
catch( Exception badloc3454 )
{
badloc3454.printStackTrace();
}
}
else // backspace when already empty -> close it
{
// Remove the point (or space) character :
try
{
editorPanel.getEditor().getDocument().remove(insertPosition-1,1 );
}
catch( Exception e234876 ){}
if( ( isVisible() ) && (thisCodeCompletion != null ) )
{
setVisible(false);
terminate();
}
}
}
else // escape was pressed, close it :
if( charValue == 27 )
{
// just close it :
if( ( isVisible() ) && (thisCodeCompletion != null ) )
{
setVisible(false);
terminate();
}
}
else // if Return (CR of LF) was pressed, append the current selection and close it :
if( ( charValue == 13 ) || ( charValue == 10 ) )
{
try
{
// First remove the current buffer content, if any is there :
if( stringBuffer.length() > 0 )
{
// remove the old stringbuffer from the editor :
editorPanel.getEditor().getDocument().remove(insertPosition,stringBuffer.length() );
stringBuffer.setLength(0);
}
// Then take the current selection and append this one :
final int selectedIndex = entryList.getSelectedIndex();
if( selectedIndex >= 0 )
{
final StringListEntry selectedEntry = (StringListEntry)entryList.getModel().getElementAt( selectedIndex );
// Insert the comparatorstring of the selected entry.
// If it's a leaf, we can add a ";" sign, otherwise
// we can add a point, which then triggers the next import CC.
// Exception: After the 1.5 static import, we insert a new space instead of a point.
String insertionString = null;
if( selectedEntry.is_LeafContent )
{
insertionString = selectedEntry.getComparatorString() + ";";
}
else
{
if( selectedEntry.getComparatorString().equals("static") )
{
insertionString = selectedEntry.getComparatorString() + " ";
}
else
{
insertionString = selectedEntry.getComparatorString() + ".";
}
}
// First close it :
if( ( isVisible() ) && (thisCodeCompletion != null ) )
{
setVisible(false);
}
// then insert the text, which again can force the popup to be displayed,
// if f.ex. it ends with a point:
final Style style = StylesFactory.GetEditorTextStyle();
editorPanel.getEditor().getDocument().insertString(insertPosition,insertionString,style );
}
}
catch( Exception badloc6354 )
{
badloc6354.printStackTrace();
}
// Close it :
if( ( isVisible() ) && (thisCodeCompletion != null ) )
{
setVisible(false);
terminate();
}
}
else
{
if( stringBuffer.length() < 1024 )
{
// If the typed character has not been a java-identifier-character,
// close the popup :
if( Character.isJavaIdentifierPart(ch) )
{
try
{
// remove the old stringbuffer from the editor :
editorPanel.getEditor().getDocument().remove(insertPosition,stringBuffer.length() );
// update the stringBuffer and the popup selection :
stringBuffer.append(ch);
local_selectBestMatchingEntry();
// insert the new stringbuffer content :
final Style style = StylesFactory.GetEditorTextStyle();
editorPanel.getEditor().getDocument().insertString(insertPosition,stringBuffer.toString(),style );
}
catch( Exception badloc12376 )
{
badloc12376.printStackTrace();
}
}
else // If the typed character has not been a java-identifier-character,close the popup :
{
if( ( isVisible() ) && (thisCodeCompletion != null ) )
{
setVisible(false); // first close the cc, as a follower could come up
try
{
// remove the old stringbuffer from the editor :
editorPanel.getEditor().getDocument().remove(insertPosition,stringBuffer.length() );
// update the stringBuffer and the popup selection :
stringBuffer.append(ch);
// insert the new stringbuffer content :
final Style style = StylesFactory.GetEditorTextStyle();
editorPanel.getEditor().getDocument().insertString(insertPosition,stringBuffer.toString(),style );
}
catch( Exception badloc12376 )
{
badloc12376.printStackTrace();
}
terminate();
}
}
}
}
} // keyTyped
private void local_selectBestMatchingEntry()
{
if( stringBuffer.length() > 0 )
{
final String compareBasis = stringBuffer.toString();
int indexForSelection = 0;
while( indexForSelection < entryList.getModel().getSize() )
{
final StringListEntry selectedEntry = (StringListEntry)entryList.getModel().getElementAt( indexForSelection );
final String compareString = selectedEntry.getComparatorString();
if( compareBasis.compareToIgnoreCase(compareString) <= 0 )
{
break; // found the best matching entry
}
else
{
indexForSelection++;
}
}
// select and scroll to the selected line :
selectAndScrollToListIndex( entryList, indexForSelection );
}
} // selectBestMatchingEntry
}); // KeyAdapter
// Select and close on double clicks :
this.entryList.addMouseListener( new MouseAdapter()
{
public void mouseClicked( MouseEvent e )
{
if( e.getClickCount() > 1 ) // double click required
{
final int selectedIndex = entryList.getSelectedIndex();
if( selectedIndex >= 0 )
{
final StringListEntry selectedEntry = (StringListEntry)entryList.getModel().getElementAt( selectedIndex );
selectedEntry.run();
// Remove all - give the GC a better chance :
if( ( isVisible() ) && (thisCodeCompletion != null ) )
{
setVisible(false);
terminate();
}
}
}
}
});
// close it, when it looses the focus :
this.entryList.addFocusListener( new FocusAdapter()
{
public void focusLost( FocusEvent e )
{
// Remove all - give the GC a better chance :
if( ( isVisible() ) && (thisCodeCompletion != null ) )
{
setVisible(false);
terminate();
}
}
});
// The list is complete including attached listeners, now show it.
// No JavaDoc available -> last parm clear.
this.showPopup( entryList,listModel,"",false,false,false );
} // if
} // showImportCodeCompletionPopup
/**
* Shows the Parameter CodeCompletion popup. Swing-Thread-Safe.
*/
private void showParameterCodeCompletionPopup( final CodeCompletionListEntry[] codeCompletionListEntryArray,
final String popupTitle )
{
// Only show it, if we have some items to display :
if( codeCompletionListEntryArray.length > 0 )
{
final CodeCompletionListModel listModel = new CodeCompletionListModel(codeCompletionListEntryArray,this.fileStructureDescriptionManager);
listModel.setShowOnlyMatches( false ); // not supported for parameter cc, only for method/attribute cc
this.entryList = new JList(listModel);
this.entryList.setCellRenderer( new CodeCompletionListCellRenderer() );
// Keypress reaction: forward letters and numbers,
// close on return or other keys :
this.entryList.addKeyListener( new KeyAdapter()
{
public void keyTyped(KeyEvent e)
{
final char ch = e.getKeyChar();
final int charValue = (int)ch;
if( charValue == 8 ) // backspace was pressed
{
if( stringBuffer.length() > 0 )
{
try
{
// remove the old stringbuffer from the editor :
editorPanel.getEditor().getDocument().remove(insertPosition,stringBuffer.length() );
// update the stringBuffer and the popup selection :
stringBuffer.setLength( stringBuffer.length()-1 );
local_selectBestMatchingEntry();
// insert the new stringbuffer content :
final Style style = StylesFactory.GetEditorTextStyle();
editorPanel.getEditor().getDocument().insertString(insertPosition,stringBuffer.toString(),style );
}
catch( Exception badloc3454 )
{
badloc3454.printStackTrace();
}
}
else // backspace when already empty -> close it
{
if( ( isVisible() ) && (thisCodeCompletion != null ) )
{
setVisible(false); // Clear the way for a possible follower cc
// Remove the point character :
try
{
editorPanel.getEditor().getDocument().remove(insertPosition-1,1 );
}
catch( Exception e234876 ){}
terminate();
}
}
}
else // if escape, return or another lowvalue character was pressed, close it :
if( charValue < 32 )
{
// just close it :
if( ( isVisible() ) && (thisCodeCompletion != null ) )
{
setVisible(false);
terminate();
}
}
else
if( ( ch == ')' ) || ( ch == '.' ) ) // closing bracket or point was pressed -> close
{
// Close it, but append the bracket too :
if( ( isVisible() ) && (thisCodeCompletion != null ) )
{
try
{
setVisible(false); // Clear the way for a possible follower cc frame.
// remove the old stringbuffer from the editor :
editorPanel.getEditor().getDocument().remove(insertPosition,stringBuffer.length() );
// update the stringBuffer and the popup selection :
stringBuffer.append(ch);
// insert the new stringbuffer content :
final Style style = StylesFactory.GetEditorTextStyle();
editorPanel.getEditor().getDocument().insertString(insertPosition,stringBuffer.toString(),style );
}
catch( Exception badloc12376 )
{
badloc12376.printStackTrace();
}
terminate();
}
}
else
{
if( stringBuffer.length() < 1024 )
{
try
{
// remove the old stringbuffer from the editor :
editorPanel.getEditor().getDocument().remove(insertPosition,stringBuffer.length() );
// update the stringBuffer and the popup selection :
local_selectBestMatchingEntry();
stringBuffer.append(ch);
// insert the new stringbuffer content :
final Style style = StylesFactory.GetEditorTextStyle();
editorPanel.getEditor().getDocument().insertString(insertPosition,stringBuffer.toString(),style );
}
catch( Exception badloc12376 )
{
badloc12376.printStackTrace();
}
}
}
} // keyTyped
private void local_selectBestMatchingEntry()
{
if( stringBuffer.length() > 0 )
{
final String compareBasis = stringBuffer.toString();
int indexForSelection = 0;
while( indexForSelection < entryList.getModel().getSize() )
{
final CodeCompletionListEntry selectedEntry = (CodeCompletionListEntry)entryList.getModel().getElementAt( indexForSelection );
final String compareString = selectedEntry.getComparatorString();
if( compareBasis.compareToIgnoreCase(compareString) <= 0 )
{
break; // found the best matching entry
}
else
{
indexForSelection++;
}
}
// select and scroll to the selected line :
selectAndScrollToListIndex( entryList, indexForSelection );
}
} // selectBestMatchingEntry
}); // KeyAdapter
// close it, when it looses the focus :
this.entryList.addFocusListener( new FocusAdapter()
{
public void focusLost( FocusEvent e )
{
// Remove all - give the GC a better chance :
if( ( isVisible() ) && (thisCodeCompletion != null ) )
{
setVisible(false);
terminate();
}
}
});
// The list is complete including attached listeners, now show it :
// JavaDoc available -> last parm set.
this.showPopup( this.entryList,listModel,popupTitle,true,false,false );
} // if
} // showParameterCodeCompletionPopup
/**
* This is called from showCodeCompletionPopup and from
* showParameterCodeCompletionPopup, after the list has been
* created with all needed contents and listeners.
*/
private void showPopup( final JList list,
final ListModel listModel,
final String popupTitle,
final boolean javaDocIsAvailable,
final boolean isJumpSupported,
final boolean isMethodAttributeCC )
{
// Change the coordinatesystem from editorpanel to screen :
Point globalPoint = new Point( insertionPoint );
SwingUtilities.convertPointToScreen(globalPoint,editorPanel);
final Font treeFont = UIManager.getFont("Tree.font");
// treeFont is used for the labels with selfdrawing items,
// cause these also use the treefont.
final int gap = treeFont.getSize();
// preferred location:
int xp = globalPoint.x - gap;
int yp = globalPoint.y + 2*gap;
this.splitPane = new JSplitPane( JSplitPane.VERTICAL_SPLIT );
this.originalDividerSize = this.splitPane.getDividerSize();
// The dividersize is set to zero, while the javadoc is turned off.
this.mainPanel = new JPanel( new BorderLayout(0,0) );
this.mainPanel.setMinimumSize( new Dimension(36*gap,20*gap) );
this.splitWrapPanel = new JPanel( new BorderLayout(0,0) );
this.splitWrapPanel.add( this.splitPane, BorderLayout.CENTER );
this.getContentPane().setLayout( new BorderLayout(0,0) );
this.getContentPane().add( this.splitWrapPanel, BorderLayout.CENTER );
this.splitPane.setLeftComponent(mainPanel);
final int bw = 1+gap/3;
final Border inb = BorderFactory.createEmptyBorder(bw,bw,bw,bw);
final Border outb = BorderFactory.createRaisedBevelBorder();
this.splitWrapPanel.setBorder( BorderFactory.createCompoundBorder(outb, inb) );
// Note: This border is very important, otherwise the resize methods
// of the parentobject (JResizableWindow) wouldn't work.
// JResizableWindow will control the mainpanel's cursor.
// If a title has been passed, display it at the top, and also add the top switches:
if( popupTitle != null )
{
if( popupTitle.length() > 0 )
{
JPanel titlePanel = new JPanel( new BorderLayout(gap/2,0) );
// The title:
JContrastLabel titleLabel = new JContrastLabel(" " + popupTitle );
titleLabel.setFont(treeFont);
// The search field ( added only for the attribute/method cc )
if( isMethodAttributeCC )
{
final CodeCompletionListModel ccListModel = (CodeCompletionListModel)listModel;
final JContrastLabel showMatchesLabel =
new JContrastLabel( Language.Translate("Matches only") );
showMatchesLabel.setFont(treeFont);
showMatchesLabel.setCursor( new Cursor( Cursor.HAND_CURSOR) );
showMatchesLabel.setIcon( (this.doShowOnlyMatches) ? OnIcon : OffIcon );
// dynamic behaviour :
showMatchesLabel.addMouseListener( new MouseAdapter()
{
public void mouseReleased( MouseEvent e )
{
// toggle
if(showMatchesLabel.getIcon() == OnIcon)
{
// turn it off:
showMatchesLabel.setIcon( OffIcon );
ccListModel.setShowOnlyMatches(false);
selectBestMatchingEntry( list,ccListModel,stringBuffer.toString() );
doShowOnlyMatches = false;
iniFile.setBooleanValue(DoShowOnlyMatchesIniFileKey,false);
}
else
{
showMatchesLabel.setIcon( OnIcon );
ccListModel.setShowOnlyMatches(true);
selectBestMatchingEntry( list,ccListModel,stringBuffer.toString() );
doShowOnlyMatches = true;
iniFile.setBooleanValue(DoShowOnlyMatchesIniFileKey,true);
}
}
});
JPanel titleCombiPanel = new JPanel( new BorderLayout(0,0) );
titleCombiPanel.add(showMatchesLabel, BorderLayout.SOUTH );
// Add the titleLabel on the top, and the search part below it:
titleCombiPanel.add( titleLabel, BorderLayout.CENTER );
// Add that combi panel:
titlePanel.add( titleCombiPanel, BorderLayout.WEST );
} // if
else
{
// Only add the titleLabel:
titlePanel.add(titleLabel, BorderLayout.WEST);
}
// Add them:
// For classmethod and parameter cc, which have a CodeCompletionListModel,
// we can display attribute and method on/off switches :
if( listModel instanceof CodeCompletionListModel )
{
final CodeCompletionListModel ccListModel = (CodeCompletionListModel)listModel;
JPanel rightTitlePanel = new JPanel( new BorderLayout(gap/2,0) );
JPanel left_RightTitlePanel = new JPanel( new BorderLayout(0,0) );
JPanel right_RightTitlePanel = new JPanel( new BorderLayout(0,0) );
rightTitlePanel.add( left_RightTitlePanel, BorderLayout.CENTER );
rightTitlePanel.add( right_RightTitlePanel, BorderLayout.EAST );
titlePanel.add( rightTitlePanel, BorderLayout.EAST );
final JContrastLabel showAttributesLabel = new JContrastLabel( Language.Translate("Attributes") + " " );
showAttributesLabel.setFont(treeFont);
showAttributesLabel.setCursor( new Cursor( Cursor.HAND_CURSOR) );
showAttributesLabel.setIcon( OnIcon );
// dynamic behaviour :
showAttributesLabel.addMouseListener( new MouseAdapter()
{
public void mouseReleased( MouseEvent e )
{
// toggle
if(showAttributesLabel.getIcon() == OnIcon)
{
// turn it off:
showAttributesLabel.setIcon( OffIcon );
ccListModel.setShowAttributes( false );
}
else
{
showAttributesLabel.setIcon( OnIcon );
ccListModel.setShowAttributes( true );
}
}
});
left_RightTitlePanel.add( showAttributesLabel, BorderLayout.CENTER );
final JContrastLabel showInheritedObjectsLabel = new JContrastLabel( Language.Translate("Inherited") + " " );
showInheritedObjectsLabel.setFont(treeFont);
showInheritedObjectsLabel.setCursor( new Cursor( Cursor.HAND_CURSOR) );
showInheritedObjectsLabel.setIcon( OnIcon );
// dynamic behaviour :
showInheritedObjectsLabel.addMouseListener( new MouseAdapter()
{
public void mouseReleased( MouseEvent e )
{
// toggle
if(showInheritedObjectsLabel.getIcon() == OnIcon)
{
// turn it off:
showInheritedObjectsLabel.setIcon( OffIcon );
ccListModel.setShowInheritedObjects( false );
}
else
{
showInheritedObjectsLabel.setIcon( OnIcon );
ccListModel.setShowInheritedObjects( true );
}
}
});
left_RightTitlePanel.add( showInheritedObjectsLabel, BorderLayout.SOUTH );
final JContrastLabel showMethodsLabel = new JContrastLabel( Language.Translate("Methods") + " " );
showMethodsLabel.setFont(treeFont);
showMethodsLabel.setCursor( new Cursor( Cursor.HAND_CURSOR) );
showMethodsLabel.setIcon( OnIcon );
// dynamic behaviour :
showMethodsLabel.addMouseListener( new MouseAdapter()
{
public void mouseReleased( MouseEvent e )
{
// toggle
if(showMethodsLabel.getIcon() == OnIcon)
{
// turn it off:
showMethodsLabel.setIcon( OffIcon );
ccListModel.setShowMethods( false );
}
else
{
showMethodsLabel.setIcon( OnIcon );
ccListModel.setShowMethods( true );
}
}
});
right_RightTitlePanel.add( showMethodsLabel, BorderLayout.CENTER );
final JContrastLabel showProjectOnlyLabel = new JContrastLabel( Language.Translate("Project Only") + " ");
showProjectOnlyLabel.setFont(treeFont);
showProjectOnlyLabel.setCursor( new Cursor( Cursor.HAND_CURSOR) );
showProjectOnlyLabel.setIcon( OffIcon );
// dynamic behaviour :
showProjectOnlyLabel.addMouseListener( new MouseAdapter()
{
public void mouseReleased( MouseEvent e )
{
// toggle
if(showProjectOnlyLabel.getIcon() == OnIcon)
{
// turn it off:
showProjectOnlyLabel.setIcon( OffIcon );
ccListModel.setShowProjectObjectsOnly( false );
}
else
{
showProjectOnlyLabel.setIcon( OnIcon );
ccListModel.setShowProjectObjectsOnly( true );
}
}
});
right_RightTitlePanel.add( showProjectOnlyLabel, BorderLayout.SOUTH );
} // if
mainPanel.add( titlePanel, BorderLayout.NORTH );
}
}
final JScrollPane scrollPane = new JScrollPane(list);
scrollPane.setCursor( new Cursor( Cursor.DEFAULT_CURSOR) );
mainPanel.add( scrollPane, BorderLayout.CENTER );
JPanel buttonPanel = new JPanel( new BorderLayout(0,0) );
mainPanel.add( buttonPanel, BorderLayout.SOUTH );
JPanel jumpPanel = new JPanel( new BorderLayout(0,0) );
jumpPanel.setBorder( BorderFactory.createEmptyBorder(0,0,0,0) );
buttonPanel.add( jumpPanel, BorderLayout.NORTH );
if( isJumpSupported )
{
final JContrastLabel jumpLabel = new JContrastLabel( Language.Translate("to the selected entry.") );
jumpLabel.setFont(treeFont);
jumpLabel.setIcon( JumpIcon );
jumpLabel.setCursor( new Cursor( Cursor.HAND_CURSOR) );
// dynamic behaviour :
jumpLabel.addMouseListener( new MouseAdapter()
{
public void mouseReleased( MouseEvent e )
{
Object selectedObject = list.getSelectedValue();
if( selectedObject != null )
{
if( selectedObject instanceof CodeCompletionListEntry )
{
setVisible(false); // close this popup
// call freeMemory on the bottom of this block
CodeCompletionListEntry selectedEntry = (CodeCompletionListEntry)selectedObject;
if( selectedEntry.isMethodEntry() )
{
jumpToMethodInFile( selectedEntry.getMethodFSD(), selectedEntry.getParentFSD() );
}
else
if( selectedEntry.isFieldEntry() )
{
jumpToFieldInFile( selectedEntry.getFieldFSD(), selectedEntry.getParentFSD() );
}
else
if( selectedEntry.isStaticInnerClassEntry() )
{
jumpToStaticInnerClassInFile( selectedEntry.getStaticInnerClassFSD(), selectedEntry.getParentFSD() );
}
else
{
Log.Info("CC jumpLabel handler: neither method nor field");
}
terminate();
}
else
{
Log.Error("CC jumpLabel handler: wrong instance: " +
selectedObject.getClass().getName() );
}
}
else
{
Log.Warn("CC jumpLabel handler: selectedObject is NULL");
}
}
});
jumpPanel.add( jumpLabel, BorderLayout.WEST );
} // if jump is supported
boolean javaDocIsActive = this.iniFile.getBooleanValue(JavaCheckBoxSelectionIniFileKey,false);
// Give the mainpanel a constant witdh (with regard to the current fontsize)
// but make the height dependent from the number of entries
// up to a certain limit :
int popupVerticalCount = list.getModel().getSize();
if( popupVerticalCount < 8 ) popupVerticalCount = 8;
if( popupVerticalCount > 16 ) popupVerticalCount = 16;
int horizontalSize = 36*gap;
final int javaDocPanelHeight = 8 * gap;
final int ccPanelHeight = 3*gap + (popupVerticalCount*5*gap)/4;
int verticalSize = 0;
if( javaDocIsActive && javaDocIsAvailable )
{
verticalSize = ccPanelHeight + javaDocPanelHeight;
this.splitPane.setDividerSize(this.originalDividerSize);
}
else
{
verticalSize = ccPanelHeight;
this.splitPane.setDividerSize(0);
}
final NonFocusBackgroundTextArea javaDocArea = new NonFocusBackgroundTextArea("");
javaDocArea.setBorder( BorderFactory.createEmptyBorder(0,gap,0,gap) );
// gives space on the sides without going outof sync with the background.
javaDocArea.setEditable(false);
javaDocArea.setRequestFocusEnabled(false);
list.addListSelectionListener( new ListSelectionListener()
{
public void valueChanged(ListSelectionEvent e)
{
if( javaDocArea.isVisible() )
{
Object selectedObject = list.getSelectedValue();
if( selectedObject != null )
{
if( selectedObject instanceof CodeCompletionListEntry )
{
CodeCompletionListEntry selectedEntry = (CodeCompletionListEntry)selectedObject;
String javaDocText = selectedEntry.getJavaDoc();
javaDocArea.setText( javaDocText );
javaDocArea.setCaretPosition(0);
}
}
}
}
});
final JContrastLabel javaDocSwitchLabel = new JContrastLabel( Language.Translate("Show Javadoc Comments") + " ");
javaDocSwitchLabel.setFont(treeFont);
javaDocSwitchLabel.setCursor( new Cursor( Cursor.HAND_CURSOR) );
if( javaDocIsActive )
javaDocSwitchLabel.setIcon( OnIcon ); else
javaDocSwitchLabel.setIcon( OffIcon);
boolean rememberSize = this.iniFile.getBooleanValue(RememberWindowSizeIniFileKey,false);
final JContrastLabel rememberSizeLabel = new JContrastLabel( Language.Translate("Remember Size") + " " );
rememberSizeLabel.setFont(treeFont);
rememberSizeLabel.setCursor( new Cursor( Cursor.HAND_CURSOR) );
if( rememberSize )
rememberSizeLabel.setIcon( OnIcon ); else
rememberSizeLabel.setIcon( OffIcon);
// dynamic behaviour :
rememberSizeLabel.addMouseListener( new MouseAdapter()
{
public void mouseReleased( MouseEvent e )
{
// toggle
if(rememberSizeLabel.getIcon() == OnIcon)
{
// turn it off:
rememberSizeLabel.setIcon( OffIcon );
iniFile.setBooleanValue(RememberWindowSizeIniFileKey,false);
}
else
{
rememberSizeLabel.setIcon( OnIcon );
iniFile.setBooleanValue(RememberWindowSizeIniFileKey,true);
}
}
});
JPanel switchPanel = new JPanel( new BorderLayout(0,0) );
switchPanel.add( javaDocSwitchLabel, BorderLayout.WEST );
switchPanel.add( rememberSizeLabel, BorderLayout.EAST );
switchPanel.setBorder( BorderFactory.createEmptyBorder(0,0,0,0) );
buttonPanel.add( switchPanel, BorderLayout.CENTER );
// Only add the javadoc subpanel, if JavaDoc also is available.
// It is available for method/attribute CC, but not for import cc.
if( javaDocIsAvailable )
{
JPanel javaDocPanel = new JPanel( new BorderLayout(0,0) );
splitPane.setRightComponent(javaDocPanel);
this.buildTheJavaDocPanel( javaDocPanel,gap,javaDocPanelHeight,horizontalSize,
javaDocArea,list,javaDocIsActive,javaDocSwitchLabel );
}
// Set the mainpanels preferred size :
splitPane.setDividerLocation(ccPanelHeight-gap*3);
splitWrapPanel.setSize( new Dimension( horizontalSize,verticalSize ) );
splitWrapPanel.setPreferredSize( new Dimension( horizontalSize,verticalSize ) );
// and let Swing set all preferred sizes :
this.pack();
// Overwrite the default values by stored values,if the flag is set and
// if values were found on the inifile too :
if( rememberSize )
{
int ws = iniFile.getIntegerValue(WindowWidthIniFileKey,this.getSize().width);
int hs = iniFile.getIntegerValue(WindowHeightIniFileKey,this.getSize().height);
int sp = iniFile.getIntegerValue(WindowSplitLocationIniFileKey,this.splitPane.getDividerLocation());
this.setSize(ws,hs);
this.splitPane.setDividerLocation(sp);
// and adjust the cachd values too (so that the location is set correctly below) :
horizontalSize = ws;
verticalSize = hs;
}
// Set the location :
// preferred would be (xp,yp), but if the JWindow would go
// outside the screen, we have to choose an alternate location,
// so test the location and change if needed :
final Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
// vertical location :
if( yp + verticalSize > screen.height )
{
// show it above the line instead :
yp = globalPoint.y - verticalSize;
}
// horizontal location
if( xp + horizontalSize + gap > screen.width )
{
// move it to the left :
xp = screen.width - horizontalSize - gap;
}
// now set the location :
setLocation(xp,yp);
setVisible(true);
list.setSelectedIndex(0);
list.requestFocusInWindow(); // new, to be tried out
/* temporarily outcommented ( time consumation? )
list.requestDefaultFocus();
list.requestFocus();
*/
} // showPopup
/**
* Overwritten for additionally storing state attributes
* when its made invisible.
*/
public void setVisible( boolean state )
{
if( !state )
{
if( iniFile != null )
{
if( iniFile.getBooleanValue(RememberWindowSizeIniFileKey,false) )
{
iniFile.setIntegerValue(WindowWidthIniFileKey,getSize().width );
iniFile.setIntegerValue(WindowHeightIniFileKey,getSize().height );
iniFile.setIntegerValue(WindowSplitLocationIniFileKey,splitPane.getDividerLocation() );
}
}
}
super.setVisible(state);
if( state )
{
EventQueue.invokeLater( new Runnable()
{
public void run()
{
toFront();
}
});
}
else
{
// This especially is required for JDK 1.4 or later, because in these
// JVM's, the editor wouldn't get the focus back after the CodeCompletion
// popup has been closed.
if( editorPanel != null )
{
if( editorPanel.getEditor() != null )
{
editorPanel.getEditor().requestFocus();
editorPanel.getEditor().setSelectionStart( editorPanel.getEditor().getCaretPosition() );
editorPanel.getEditor().setSelectionEnd( editorPanel.getEditor().getCaretPosition() );
}
}
}
} // setVisible
/**
* Adds the panel, which contains the JavaDoc checkbox,
* and the JavaDoc content panel, if that checkbox is selected,
* and as long as it's selected.
*/
private void buildTheJavaDocPanel( final JPanel basisPanel,
final int fontSize,
final int javaDocPanelHeight,
final int horizontalSize,
final JTextArea javaDocArea,
final JList list,
final boolean javaDocIsActive,
final JLabel javaDocSwitchLabel )
{
final JFrame thisWindow = this;
final Font treeFont = UIManager.getFont("Tree.font");
// treeFont is used for the labels with selfdrawing items,
// cause these also use the treefont.
final JScrollPane javaDocScrollPane = new JScrollPane( javaDocArea );
// Set the preferred size of the javaDoc scrollpane :
// Same width like the mainpanel of the popup, 4 fontsizes height.
// This size will be set by the pack() call of the caller.
javaDocScrollPane.setPreferredSize( new Dimension( horizontalSize,javaDocPanelHeight ) );
javaDocScrollPane.setRequestFocusEnabled(false);
basisPanel.add( javaDocScrollPane, BorderLayout.CENTER );
// Set visibility depending of the javaDocSwitchLabel state :
javaDocScrollPane.setVisible( (javaDocSwitchLabel.getIcon() == OnIcon) ); // both containers must
javaDocArea.setVisible( (javaDocSwitchLabel.getIcon() == OnIcon) ); // change visibility
// dynamic behaviour :
javaDocSwitchLabel.addMouseListener( new MouseAdapter()
{
public void mouseReleased( MouseEvent e )
{
// toggle
if(javaDocSwitchLabel.getIcon() == OnIcon)
{
// Turn it off:
int currentJavaDocPanelHeight = javaDocScrollPane.getSize().height;
javaDocSwitchLabel.setIcon( OffIcon );
javaDocScrollPane.setVisible(false); // both containers must
javaDocArea.setVisible(false); // change visibility
iniFile.setBooleanValue(JavaCheckBoxSelectionIniFileKey,false);
// Set the divider location to maximum:
splitPane.setDividerLocation(1.0d); // 1.0d == set max value
splitPane.setDividerSize(0);
// Make the window height smaller :
Dimension actualSize = thisWindow.getSize();
int newHeight = actualSize.height - currentJavaDocPanelHeight;
if( newHeight < 4*fontSize ) newHeight = 4*fontSize;
Dimension newSize = new Dimension( actualSize.width,newHeight );
thisWindow.setSize( newSize );
}
else
{
javaDocSwitchLabel.setIcon( OnIcon );
javaDocScrollPane.setVisible(true); // both containers must
javaDocArea.setVisible(true); // change visibility
iniFile.setBooleanValue(JavaCheckBoxSelectionIniFileKey,true);
// Reset the divider :
splitPane.setDividerSize(originalDividerSize);
// Make the window height bigger :
Dimension actualSize = thisWindow.getSize();
int newHeight = actualSize.height + javaDocPanelHeight;
Dimension newSize = new Dimension( actualSize.width,newHeight );
thisWindow.setSize( newSize );
// and update the JavaDoc content:
if( javaDocArea.isVisible() )
{
Object selectedObject = list.getSelectedValue();
if( selectedObject != null )
{
if( selectedObject instanceof CodeCompletionListEntry )
{
CodeCompletionListEntry selectedEntry = (CodeCompletionListEntry)selectedObject;
javaDocArea.setText( selectedEntry.getJavaDoc() );
javaDocArea.setCaretPosition(0);
}
}
}
}
thisWindow.validate();
thisWindow.repaint();
}
});
// Track resizings. Adjust the splitpane position, when
// JavaDoc is turned off: Keep it at max value :
this.addComponentListener( new ComponentAdapter()
{
public void componentResized(ComponentEvent e)
{
if(javaDocSwitchLabel.getIcon() == OffIcon) // JavaDoc is turned off.
{
// Set the divider location to maximum:
splitPane.setDividerLocation(1.0d); // 1.0d == set max value
}
}
});
} // buildTheJavaDocPanel
/**
* Selects the listentry with the passed listIndex and
* tells the list view to scroll the selected line to visible.
*/
public void selectAndScrollToListIndex( final JList list,
final int listIndex )
{
if( ( listIndex >= 0 ) && ( listIndex < list.getModel().getSize() ) )
{
list.setSelectedIndex(listIndex);
final Point selectedPoint = list.indexToLocation(listIndex);
final int gap = UIManager.getFont("Label.font").getSize();
if( selectedPoint != null )
{
final Rectangle rect = new Rectangle( 0,selectedPoint.y,10,gap );
list.scrollRectToVisible( rect );
}
else
{
Log.Warn("selectedPoint was NULL");
}
}
} // selectAndScrollToListIndex
/**
* Lets the editor jump to the file containing the method given by
* methodFSD and the fsd which contains that method fsd.
* If required, the search goes backward in the parentchain of the
* parentFSD, which is required, if the method isnt toplevel in parentFSD.
*/
private void jumpToMethodInFile( final FileStructureDescriptionForMethod methodFSD,
final FileStructureDescription parentFSD )
{
Runnable jumper = new Runnable()
{
public void run()
{
jumpToMethodInFile_OutsideEDT(methodFSD,parentFSD);
}
};
ThreadEngine.getInstance().addRunnable(jumper,"JumpToMethod");
}
/**
* Called by jumpToMethodInFile in a threadengine context.
*/
private void jumpToMethodInFile_OutsideEDT( final FileStructureDescriptionForMethod methodFSD,
final FileStructureDescription parentFSD )
{
int lineNumber = methodFSD.name.startLine - 1;
// See if the methodFSD is toplevel, for which case we can make it
// fast without having to create transient contents:
if( this.isMethodFSDContainedInFSD(methodFSD,parentFSD) )
{
// Look in the ProjectFilesTree first :
boolean fileWasFoundAndDisplayed =
this.projectFrame.getProjectFilesManager().getProjectFilesTree().showLeafByTreePathName(
parentFSD.fullyQualifiedClassNameBuffer,
parentFSD.extension,
lineNumber,
0,
true );
// If it was not found there, look in the librariestree :
if( !fileWasFoundAndDisplayed )
{
fileWasFoundAndDisplayed = this.projectFrame.getLibrariesTree().showLeafByTreePathName(
parentFSD.fullyQualifiedClassNameBuffer,
parentFSD.extension,
lineNumber,
0,
true );
}
}
else
{
// Search the parentchain, until the methodFSD has been found :
// Temporarily load the transient attributes (which also the parent chain belongs to)
this.fileStructureDescriptionManager.updateTransientFieldsFor(parentFSD);
FileStructureDescription basisFSD = null;
for( int i=0; i < parentFSD.parentDescriptions.length; i++ )
{
if( this.isMethodFSDContainedInFSD(methodFSD,parentFSD.parentDescriptions[i]) )
{
basisFSD = parentFSD.parentDescriptions[i];
break;
}
} // for
// Prevent memory explosion: Free the transient fields again :
this.fileStructureDescriptionManager.releaseTransientFieldsFor(parentFSD);
if( basisFSD != null ) // found it in a parent fsd
{
// Look in the ProjectFilesTree first :
boolean fileWasFoundAndDisplayed =
this.projectFrame.getProjectFilesManager().getProjectFilesTree().showLeafByTreePathName(
basisFSD.fullyQualifiedClassNameBuffer,
basisFSD.extension,
lineNumber,
0,
false );
// If it was not found there, look in the librariestree :
if( !fileWasFoundAndDisplayed )
{
fileWasFoundAndDisplayed = this.projectFrame.getLibrariesTree().showLeafByTreePathName(
basisFSD.fullyQualifiedClassNameBuffer,
basisFSD.extension,
lineNumber,
0,
true );
}
}
} // else
} // jumpToMethodInFile
/**
* Analog to jumpToMethodInFile, but for attributes.
*/
private void jumpToFieldInFile( final FileStructureDescriptionForField fieldFSD,
final FileStructureDescription parentFSD )
{
Runnable jumper = new Runnable()
{
public void run()
{
jumpToFieldInFile_OutsideEDT(fieldFSD,parentFSD);
}
};
ThreadEngine.getInstance().addRunnable(jumper,"JumpToField");
}
/**
* Called by jumpToFieldInFile in a threadengine context.
*/
private void jumpToFieldInFile_OutsideEDT( final FileStructureDescriptionForField fieldFSD,
final FileStructureDescription parentFSD )
{
int lineNumber = fieldFSD.simpleClassName.startLine - 1;
// See if the fieldFSD is toplevel, for which case we can make it
// fast without having to create transient contents:
if( this.isFieldFSDContainedInFSD(fieldFSD,parentFSD) )
{
// Look in the ProjectFilesTree first :
boolean fileWasFoundAndDisplayed =
this.projectFrame.getProjectFilesManager().getProjectFilesTree().showLeafByTreePathName(
parentFSD.fullyQualifiedClassNameBuffer,
parentFSD.extension,
lineNumber,
0,
false );
// If it was not found there, look in the librariestree :
if( !fileWasFoundAndDisplayed )
{
fileWasFoundAndDisplayed = this.projectFrame.getLibrariesTree().showLeafByTreePathName(
parentFSD.fullyQualifiedClassNameBuffer,
parentFSD.extension,
lineNumber,
0,
true );
}
}
else
{
// Search the parentchain, until the methodFSD has been found :
// Temporarily load the transient attributes (which also the parent chain belongs to)
this.fileStructureDescriptionManager.updateTransientFieldsFor(parentFSD);
FileStructureDescription basisFSD = null;
for( int i=0; i < parentFSD.parentDescriptions.length; i++ )
{
if( this.isFieldFSDContainedInFSD(fieldFSD,parentFSD.parentDescriptions[i]) )
{
basisFSD = parentFSD.parentDescriptions[i];
break;
}
} // for
// Prevent memory explosion: Free the transient fields again :
this.fileStructureDescriptionManager.releaseTransientFieldsFor(parentFSD);
// If nothing has been found in the inherited parent chain, scan the chains of
// the implemented interfaces. There are as many chains as implemented interfaces
// for this class, because once we are in an interface, it only can extend another
// interface (but not extend a class or implement anything )
if( basisFSD == null )
{
FileStructureDescription workFSD = parentFSD;
final Vector interfaceVector = workFSD.implementedInterfaces;
// From each of the implemented interfaces there can start a chain
// of further *extended* child interfaces (only interface extends interface allowed) :
for( int q=0; q < interfaceVector.size(); q++ )
{
StringWithPosition interfaceEntry = (StringWithPosition)interfaceVector.elementAt(q);
String interfaceQualifier = interfaceEntry.content;
// Search this interface chain :
FileStructureDescription[] interfaceParentFSDs = CodeCompletionUtilities.GetInterfaceChainForFSD( workFSD,interfaceQualifier,this.fileStructureDescriptionManager );
for( int k=0; k < interfaceParentFSDs.length; k++ )
{
if( interfaceParentFSDs[k].containsTopLevelMemberAttributeWithName( fieldFSD.objectNameWithPosition.content ) )
{
basisFSD = interfaceParentFSDs[k];
break;
}
}
} // for
} // if
if( basisFSD != null ) // found it in a parent fsd
{
// Look in the ProjectFilesTree first :
boolean fileWasFoundAndDisplayed =
this.projectFrame.getProjectFilesManager().getProjectFilesTree().showLeafByTreePathName(
basisFSD.fullyQualifiedClassNameBuffer,
basisFSD.extension,
lineNumber,
0,
false );
// If it was not found there, look in the librariestree :
if( !fileWasFoundAndDisplayed )
{
fileWasFoundAndDisplayed =
this.projectFrame.getLibrariesTree().showLeafByTreePathName(
basisFSD.fullyQualifiedClassNameBuffer,
basisFSD.extension,
lineNumber,
0,
true );
}
}
} // else
} // jumpToFieldInFile_OutsideEDT
/**
* Analog to jumpToMethodInFile, but for static inner classes.
*/
private void jumpToStaticInnerClassInFile( final FileStructureDescription staticInnerClassFSD,
final FileStructureDescription parentFSD )
{
Runnable jumper = new Runnable()
{
public void run()
{
jumpToStaticInnerClassInFile_OutsideEDT( staticInnerClassFSD,parentFSD );
}
};
ThreadEngine.getInstance().addRunnable(jumper,"JumpToStaticInnerClass");
}
/**
* Called by jumpToFieldInFile in a threadengine context.
*/
private void jumpToStaticInnerClassInFile_OutsideEDT( final FileStructureDescription staticInnerClassFSD,
final FileStructureDescription parentFSD )
{
int lineNumber = staticInnerClassFSD.scopeStartLine;
// Look in the ProjectFilesTree first :
boolean fileWasFoundAndDisplayed =
this.projectFrame.getProjectFilesManager().getProjectFilesTree().showLeafByTreePathName(
parentFSD.fullyQualifiedClassNameBuffer,
parentFSD.extension,
lineNumber,
0,
false );
// If it was not found there, look in the librariestree :
if( !fileWasFoundAndDisplayed )
{
fileWasFoundAndDisplayed = this.projectFrame.getLibrariesTree().showLeafByTreePathName(
parentFSD.fullyQualifiedClassNameBuffer,
parentFSD.extension,
lineNumber,
0,
true );
}
}
private boolean isMethodFSDContainedInFSD( FileStructureDescriptionForMethod methodFSD,
FileStructureDescription parentFSD )
{
boolean isContained = false;
for( int i=0; i < parentFSD.methodDescriptions.size(); i++ )
{
FileStructureDescriptionForMethod mFsd = (FileStructureDescriptionForMethod)parentFSD.methodDescriptions.elementAt(i);
if( mFsd == methodFSD ) // Reference comparison
{
isContained = true;
break;
}
} // for
return isContained;
}
private boolean isFieldFSDContainedInFSD( FileStructureDescriptionForField fieldFSD,
FileStructureDescription parentFSD )
{
boolean isContained = false;
for( int i=0; i < parentFSD.fieldDescriptions.size(); i++ )
{
FileStructureDescriptionForField fFsd = (FileStructureDescriptionForField)parentFSD.fieldDescriptions.elementAt(i);
if( fFsd == fieldFSD ) // Reference comparison
{
isContained = true;
break;
}
} // for
return isContained;
}
public class TitlePanel extends JPanel
{
public TitlePanel( final String title )
{
super( new BorderLayout() );
Color fgColor = UIManager.getColor("Menu.foreground");
Color bgColor = UIManager.getColor("Menu.background");
Color borderColor = null;
// give a small contrast :
int fgGray = ( fgColor.getRed() + fgColor.getGreen() + fgColor.getBlue() )/3;
int bgGray = ( bgColor.getRed() + bgColor.getGreen() + bgColor.getBlue() )/3;
if( fgGray > bgGray )
{
fgColor = this.slightlyBrighter(fgColor);
bgColor = this.slightlyDarker(bgColor);
borderColor = this.slightlyDarker(bgColor);
} else
{
fgColor = this.slightlyDarker(fgColor);
bgColor = this.slightlyBrighter(bgColor);
borderColor = this.slightlyBrighter(bgColor);
}
this.setForeground( fgColor );
this.setBackground( bgColor );
final Border inb = BorderFactory.createEmptyBorder(0,4,0,4);
final Border outb = BorderFactory.createLineBorder(borderColor);
this.setBorder( BorderFactory.createCompoundBorder(outb,inb) );
final JContrastLabel titleLabel = new JContrastLabel(title);
titleLabel.setForeground( fgColor );
titleLabel.setBackground( bgColor );
titleLabel.setFont(UIManager.getFont("Menu.font"));
this.add( titleLabel, BorderLayout.CENTER );
}
private Color slightlyDarker( final Color c )
{
int r = c.getRed() - 24; if( r < 0 ) r = 0;
int g = c.getGreen() - 24; if( g < 0 ) g = 0;
int b = c.getBlue() - 24; if( b < 0 ) b = 0;
return new Color(r,g,b);
}
private Color slightlyBrighter( final Color c )
{
int r = c.getRed() + 24; if( r > 255 ) r = 255;
int g = c.getGreen() + 24; if( g > 255 ) g = 255;
int b = c.getBlue() + 24; if( b > 255 ) b = 255;
return new Color(r,g,b);
}
} // inner class TitlePanel
} // CodeCompletion
|