0001: package tide.editor;
0002:
0003: import javax.swing.tree.TreeNode;
0004: import tide.utils.ParsedID;
0005: import tide.bytecode.DecompileManager;
0006: import tide.syntaxtree.ActionsForNode;
0007: import javaparser.ParserTreeNode;
0008: import snow.utils.*;
0009: import tide.editor.bookmarks.SourceBookmark;
0010: import java.lang.reflect.*;
0011: import tide.editor.linemessages.*;
0012: import tide.project.*;
0013: import tide.sources.*;
0014: import tide.compiler.CompilerOutputItem;
0015: import tide.editor.completions.*;
0016: import tide.editor.styler.*;
0017: import snow.utils.storage.*;
0018: import snow.utils.gui.*;
0019: import snow.concurrent.*;
0020: import snow.texteditor.*;
0021: import snow.Basics;
0022: import snow.datatransfer.*;
0023: import tide.classsyntax.IDChain;
0024: import tide.utils.SyntaxUtils;
0025: import tide.classsyntax.SingleClassLoader;
0026: import tide.classsyntax.*;
0027: import javax.swing.*;
0028: import java.awt.BorderLayout;
0029: import java.awt.Dimension;
0030: import java.awt.Color;
0031: import java.awt.EventQueue;
0032: import java.awt.Font;
0033: import java.awt.event.*;
0034: import javax.swing.border.*;
0035: import javax.swing.text.*;
0036: import javax.swing.event.*;
0037: import javax.swing.undo.*;
0038: import java.util.*;
0039: import java.io.*;
0040:
0041: /** Contains the edited file.
0042: + With tabs
0043: + with bracket balancing (highlight)
0044: + with custom autoreplace
0045: + with completions
0046: ..
0047: */
0048: public class EditorPanel extends JPanel {
0049: final EditorDocument doc = new EditorDocument();
0050: final JTextPane textPane = new JTextPane(doc) {
0051: /** false: avoid word wrapping BUT limit the width
0052: * true: always set to screen size BUT wraps
0053: */
0054: @Override
0055: public boolean getScrollableTracksViewportWidth() {
0056: //if(getPreferredSize().getWidth()>getSize().getWidth()+50) return false; // infinite loop...
0057: //if(getSize().getWidth()<500) return true; // causes infinite blink !
0058: return false;
0059: }
0060:
0061: @Override
0062: public Dimension getMinimumSize() {
0063: return new Dimension(500, 300);
0064: }
0065: //NO @Override
0066: // public Dimension getPreferredSize() { return new Dimension(500,300); }
0067: };
0068:
0069: final private JScrollPane editorScrollPane;
0070:
0071: final SourceEditorKit sourceEditorKit = new SourceEditorKit();
0072: final CompletionManager completionManager = new CompletionManager(
0073: this );
0074: final JavaCodeStyler codeStyler = new JavaCodeStyler(doc);
0075: final EditorDocumentFilter editorDocumentFilter = new EditorDocumentFilter(
0076: completionManager, this );
0077: private FileItem editedFile; // null if none
0078:
0079: /** Collects all the files changed by the user. before they are saved
0080: a call to saveChangedFiles() store the contents to disk AND empty this set.
0081: */
0082: final private Set<SourceFile> changedFiles = new HashSet<SourceFile>();
0083:
0084: /** Collects all the files changed by the user. Not deleted at save. Used for the recent edited sublist in the jump manager popup.
0085: */
0086: final List<SourceFile> allChangedFilesHistory = new ArrayList<SourceFile>();
0087:
0088: final private List<ChangedFilesSaveListener> changedFilesSaveListeners = new ArrayList<ChangedFilesSaveListener>();
0089:
0090: final SearchPanel searchPanel = new SearchPanel(textPane, doc);
0091:
0092: private final LinePanel linePanel = new LinePanel(this );
0093: private final StatusBar statusBar = new StatusBar();
0094:
0095: final JumpManager jumpManager;
0096: final private SourcesNavigationBar sourcesNavigationBar;
0097:
0098: public EditorPanel() {
0099: super (new BorderLayout(0, 0));
0100: setBorder(null);
0101:
0102: jumpManager = new JumpManager(this );
0103: sourcesNavigationBar = new SourcesNavigationBar(this );
0104:
0105: add(sourcesNavigationBar, BorderLayout.NORTH);
0106: JPanel southPan = new JPanel(new BorderLayout(0, 0));
0107: southPan.add(searchPanel, BorderLayout.NORTH);
0108: southPan.add(statusBar, BorderLayout.SOUTH);
0109: add(southPan, BorderLayout.SOUTH);
0110: add(linePanel, BorderLayout.WEST);
0111:
0112: textPane.setEditorKit(sourceEditorKit);
0113: textPane.setDocument(doc);
0114: doc.setDocumentFilter(editorDocumentFilter);
0115:
0116: // fixed width is important !
0117: Font ef = UIManager.getFont("CodeEditorFont");
0118: if (ef == null) {
0119: ef = new Font("Lucida Sans Typewriter", Font.PLAIN, 12);
0120: }
0121: textPane.setFont(ef);
0122:
0123: editorScrollPane = new JScrollPane(textPane);
0124: editorScrollPane.setBorder(null);
0125: editorScrollPane.getViewport().setBorder(null);
0126: add(editorScrollPane, BorderLayout.CENTER);
0127:
0128: editorScrollPane.getViewport().setBackground(
0129: textPane.getBackground());
0130:
0131: // set file as being edited when user hit some key NO, better done in the document model...
0132: /*textPane.addKeyListener(new KeyAdapter()
0133: {
0134: @Override public void keyTyped(KeyEvent ke)
0135: {
0136: setHasBeenEdited();
0137: }
0138: });*/
0139:
0140: // indicate the line
0141: textPane.addCaretListener(new CaretListener() {
0142: public void caretUpdate(CaretEvent ce) {
0143: int line = DocumentUtils
0144: .getLineNumber(doc, ce.getDot());
0145: sourcesNavigationBar.setLine(line);
0146: //lineLabel.setText(" Line "+(line+1)+" ");
0147:
0148: // cool & useful: indicate the position of matching braces !
0149: char ci = doc.getCharAt(ce.getDot());
0150:
0151: int pos = -1;
0152: if (ci == ')') {
0153: pos = DocumentUtils
0154: .getPositionOfCorrespondingOpeningBrace(
0155: '(', ')', doc, ce.getDot());
0156: } else if (ci == '}') {
0157: pos = DocumentUtils
0158: .getPositionOfCorrespondingOpeningBrace(
0159: '{', '}', doc, ce.getDot());
0160: } else if (ci == ']') // used for arrays only
0161: {
0162: pos = DocumentUtils
0163: .getPositionOfCorrespondingOpeningBrace(
0164: '[', ']', doc, ce.getDot());
0165: }
0166: /*else if(ci=='>') // not very useful if occuring in some if statement...
0167: {
0168: pos = DocumentUtils.getPositionOfCorrespondingOpeningBrace('<','>',doc, ce.getDot());
0169: }*/
0170: else if (ci == '(') {
0171: pos = DocumentUtils
0172: .getPositionOfCorrespondingClosingBrace(
0173: '(', ')', doc, ce.getDot());
0174: } else if (ci == '{') {
0175: pos = DocumentUtils
0176: .getPositionOfCorrespondingClosingBrace(
0177: '{', '}', doc, ce.getDot());
0178: } else if (ci == '[') {
0179: pos = DocumentUtils
0180: .getPositionOfCorrespondingClosingBrace(
0181: '[', ']', doc, ce.getDot());
0182: }
0183:
0184: if (pos >= 0) {
0185: try {
0186: final Object highlightTag1 = textPane
0187: .getHighlighter().addHighlight(pos,
0188: pos + 1, doc.searchHighlighter);
0189: final Object highlightTag2 = textPane
0190: .getHighlighter().addHighlight(
0191: ce.getDot(), ce.getDot() + 1,
0192: doc.searchHighlighter);
0193:
0194: // remove after an half second
0195: Thread t = new Thread() {
0196: public void run() {
0197: try {
0198: Thread.sleep(500);
0199: } catch (Exception e) {
0200: Basics.ignore(e);
0201: }
0202: EventQueue.invokeLater(new Runnable() {
0203: public void run() {
0204: textPane.getHighlighter()
0205: .removeHighlight(
0206: highlightTag1);
0207: textPane.getHighlighter()
0208: .removeHighlight(
0209: highlightTag2);
0210: }
0211: });
0212: }
0213: };
0214: t.setName("Highlighter");
0215: t.setPriority(Thread.MIN_PRIORITY);
0216: t.start();
0217: } catch (Exception e) {
0218: Basics.ignore(e);
0219: }
0220: }
0221:
0222: //codeStyler.doStyling();
0223:
0224: }
0225: });
0226:
0227: textPane.addMouseListener(new MouseAdapter() {
0228: @Override
0229: public void mousePressed(MouseEvent me) {
0230: if (me.isPopupTrigger()) {
0231: showPopup(me);
0232: return;
0233: }
0234: }
0235:
0236: @Override
0237: public void mouseReleased(MouseEvent me) {
0238: if (me.isPopupTrigger()) {
0239: showPopup(me);
0240: return;
0241: }
0242: }
0243:
0244: @Override
0245: public void mouseClicked(MouseEvent me) {
0246: mouseClickedInEditor(me);
0247: }
0248: });
0249:
0250: // Keys mapping
0251: //
0252:
0253: defineKeyboardActions();
0254:
0255: editorScrollPane.getViewport().addChangeListener(
0256: new ChangeListener() {
0257: public void stateChanged(ChangeEvent ce) {
0258: int[] bd = DocumentUtils
0259: .getVisibleDocPosBounds(textPane,
0260: editorScrollPane);
0261: codeStyler.viewBoundsChanged(bd[0], bd[1]);
0262: linePanel.viewBoundsChanged(bd[0], bd[1]);
0263: //System.out.println("visible bounds: "+bd[0]+" - "+bd[1]);
0264: }
0265: });
0266:
0267: this .registerKeyboardAction(sourcesNavigationBar.jumpAction,
0268: "JumpToLineNumber", Accelerators.jumpToLineNumber,
0269: JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
0270:
0271: MainEditorFrame.instance
0272: .addProjectListener(new ProjectListener() {
0273: public void projectHasBeenLoaded(ProjectSettings ps) {
0274: List<String> lastEd = ps.getProps()
0275: .getArrayProperty("editor_last_ed");
0276: if (lastEd != null) {
0277:
0278: for (String li : lastEd) {
0279: SourceFile sfi = (SourceFile) MainEditorFrame.instance.sourcesTreePanel
0280: .getTreeModel().quickGet(li,
0281: true); // TypeLocator.locateQuick(li)
0282: if (sfi != null) {
0283: allChangedFilesHistory.add(sfi);
0284: }
0285: }
0286: }
0287: }
0288:
0289: public void projectIsSaving(ProjectSettings ps) {
0290: List<String> lastEd = new ArrayList<String>();
0291: for (FileItem fi : CollectionUtils.takeLast(
0292: allChangedFilesHistory, 25)) {
0293: lastEd.add(fi.getJavaName());
0294: }
0295: ps.getProps().setArrayProperty(
0296: "editor_last_ed", lastEd);
0297:
0298: saveChangedFiles();
0299: }
0300:
0301: public void projectHasBeenClosed(ProjectSettings ps) {
0302: jumpManager.clear();
0303: }
0304: });
0305:
0306: } // Constructor
0307:
0308: private void defineKeyboardActions() {
0309: this .registerKeyboardAction(new ActionListener() {
0310: public void actionPerformed(ActionEvent ae) {
0311: if (doc.canUndo())
0312: doc.undo();
0313: }
0314: }, "Undo", Accelerators.undo,
0315: JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
0316:
0317: this .registerKeyboardAction(new ActionListener() {
0318: public void actionPerformed(ActionEvent ae) {
0319: if (doc.canRedo())
0320: doc.redo();
0321: }
0322: }, "Redo", Accelerators.redo,
0323: JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
0324:
0325: this .registerKeyboardAction(new ActionListener() {
0326: public void actionPerformed(ActionEvent ae) {
0327: // offers completion for local variables
0328: completionManager.controlSpacePressed(editedFile, doc,
0329: textPane.getCaretPosition());
0330: }
0331: }, "ManualCompletion", Accelerators.manualCompletion,
0332: JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
0333:
0334: this .registerKeyboardAction(new ActionListener() {
0335: public void actionPerformed(ActionEvent ae) {
0336: completionManager.controlTPressed(editedFile, doc,
0337: textPane.getCaretPosition(), false, null);
0338: }
0339: }, "InsertTypeCompletion", Accelerators.insertTypeCompletion,
0340: JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
0341:
0342: this .registerKeyboardAction(new ActionListener() {
0343: public void actionPerformed(ActionEvent ae) {
0344: completionManager.controlTPressed(editedFile, doc,
0345: textPane.getCaretPosition(), true, null);
0346: }
0347: }, "InsertTypeCompletionProjectOnly",
0348: Accelerators.insertTypeCompletionProjectOnly,
0349: JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
0350:
0351: this .registerKeyboardAction(new ActionListener() {
0352: public void actionPerformed(ActionEvent ae) {
0353: completionManager.closeLastCompletionDialog();
0354: }
0355: }, "CloseCompDialog", Accelerators.escape,
0356: JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
0357:
0358: this .registerKeyboardAction(new ActionListener() {
0359: public void actionPerformed(ActionEvent ae) {
0360: if (editedFile != null) {
0361: MainEditorFrame.instance.outputPanels
0362: .filterMessages(editedFile.getJavaName());
0363: }
0364: }
0365: }, "ViewMessages of selected",
0366: Accelerators.viewMessagesOfSelected,
0367: JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
0368:
0369: this .registerKeyboardAction(new ActionListener() {
0370: public void actionPerformed(ActionEvent ae) {
0371: saveChangedFiles();
0372: removeTab(editedFile);
0373: }
0374: }, "Close current tab", Accelerators.closeCurrentTab,
0375: JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
0376:
0377: this .registerKeyboardAction(new ActionListener() {
0378: public void actionPerformed(ActionEvent ae) {
0379: saveChangedFiles();
0380: sourcesNavigationBar.moveTab(editedFile, -1);
0381: }
0382: }, "Move tab to left", Accelerators.moveTabLeft,
0383: JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
0384:
0385: this .registerKeyboardAction(new ActionListener() {
0386: public void actionPerformed(ActionEvent ae) {
0387: saveChangedFiles();
0388: sourcesNavigationBar.moveTab(editedFile, 1);
0389: }
0390: }, "Move tab to right", Accelerators.moveTabRight,
0391: JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
0392:
0393: this .registerKeyboardAction(new ActionListener() {
0394: public void actionPerformed(ActionEvent ae) {
0395: saveChangedFiles();
0396: sourcesNavigationBar.setAsFirstTab(editedFile);
0397: }
0398: }, "Move tab to pos 1", Accelerators.moveTabPos1,
0399: JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
0400:
0401: this .registerKeyboardAction(new ActionListener() {
0402: public void actionPerformed(ActionEvent ae) {
0403: sourcesNavigationBar.selectNextTab();
0404: }
0405: }, "Select next tab", Accelerators.selectNextTab,
0406: JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
0407:
0408: this .registerKeyboardAction(new ActionListener() {
0409: public void actionPerformed(ActionEvent ae) {
0410: sourcesNavigationBar.selectPreviousTab();
0411: }
0412: }, "Select previous tab", Accelerators.selectPreviousTab,
0413: JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
0414:
0415: this .registerKeyboardAction(new ActionListener() {
0416: public void actionPerformed(ActionEvent ae) {
0417: String ft = getSelectedOrWordAtCaret();
0418: if (ft != null) {
0419: searchPanel.searchNext_calledFromPopup(ft, 0);
0420: }
0421: }
0422: }, "Select first occurence of selection",
0423: Accelerators.searchFirstOcc,
0424: JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
0425:
0426: this .registerKeyboardAction(new ActionListener() {
0427: public void actionPerformed(ActionEvent ae) {
0428: String ft = getSelectedOrWordAtCaret();
0429: if (ft != null) {
0430: searchPanel.searchPrevious_calledFromPopup(ft, -1);
0431: }
0432: }
0433: }, "Select last occurence of selection",
0434: Accelerators.searchLastOcc,
0435: JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
0436:
0437: this .registerKeyboardAction(new ActionListener() {
0438: public void actionPerformed(ActionEvent ae) {
0439: String ft = getSelectedOrWordAtCaret();
0440: if (ft != null) {
0441: searchPanel.searchNext_calledFromPopup(ft, textPane
0442: .getCaretPosition() + 1);
0443: } else {
0444: System.out.println("null word to search next");
0445: }
0446: }
0447: }, "Select next occurence of selection",
0448: Accelerators.nextOccurence,
0449: JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
0450:
0451: this .registerKeyboardAction(new ActionListener() {
0452: public void actionPerformed(ActionEvent ae) {
0453: String ft = getSelectedOrWordAtCaret();
0454: if (ft != null) {
0455: searchPanel.searchPrevious_calledFromPopup(ft,
0456: textPane.getCaretPosition() + 1);
0457: }
0458: }
0459: }, "Select previous occurence of selection",
0460: Accelerators.previousOccurence,
0461: JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
0462:
0463: this .registerKeyboardAction(new ActionListener() {
0464: public void actionPerformed(ActionEvent ae) {
0465: String selectedText = getSelectedOrWordAtCaret();
0466: if (selectedText != null) {
0467: clearAutoSelectedWords();
0468: autoSelectWords(selectedText);
0469: }
0470: }
0471: }, "Highlight all occurences",
0472: Accelerators.highlightAllOccurences,
0473: JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
0474:
0475: this .registerKeyboardAction(new ActionListener() {
0476: public void actionPerformed(ActionEvent ae) {
0477: FileItem fi = getActualDisplayedFile();
0478: if (fi != null) {
0479: int line = DocumentUtils.getLineNumber(doc,
0480: textPane.getCaretPosition());
0481: LineMessage mess = LineMessagesManager
0482: .getInstance().getMessageAfterLine(
0483: fi.getJavaName(), line + 1);
0484: if (mess != null) {
0485: MessagesTable.getInstance()
0486: .jumpToSourceForMessage(mess);
0487: }
0488: }
0489: }
0490: }, "JumpToNextMessage", Accelerators.jumpToNextMessage,
0491: JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
0492:
0493: // really useful, select some args, as int a, double b => press ALT+A and the code this.a = a; and this.b = b; is inserted at caret.
0494: this .registerKeyboardAction(new ActionListener() {
0495: public void actionPerformed(ActionEvent ae) {
0496: if (!textPane.isEditable())
0497: return;
0498:
0499: String cont = ClipboardUtils
0500: .getClipboardStringContent();
0501: if (cont == null || cont.length() == 0)
0502: return;
0503:
0504: String indent = DocumentUtils
0505: .getSpacesAtBeginning(DocumentUtils
0506: .getTextOfLineAtPosition_onlyUpToPos(
0507: doc, textPane
0508: .getCaretPosition()));
0509:
0510: StringBuilder sb = new StringBuilder();
0511: for (String argi : cont.split(",")) {
0512: argi = argi.trim();
0513: if (argi.length() == 0)
0514: continue;
0515: if (argi.endsWith(")")) {
0516: argi = argi.substring(0, argi.length() - 1);
0517: if (argi.length() == 0)
0518: continue;
0519: }
0520:
0521: int posSp = argi.lastIndexOf(' ');
0522: if (posSp > 0)
0523: argi = argi.substring(posSp + 1);
0524: sb.append("this." + argi + " = " + argi + ";\n"
0525: + indent);
0526: }
0527:
0528: getDocument().insertString(sb.toString().trim(),
0529: textPane.getCaretPosition());
0530:
0531: }
0532: }, "AssignMember", Accelerators.assignMember,
0533: JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
0534:
0535: // really useful (2), select some args, as int a, double b => press ALT+L and the code final int a; final double b; is inserted at caret.
0536: this .registerKeyboardAction(new ActionListener() {
0537: public void actionPerformed(ActionEvent ae) {
0538: if (!textPane.isEditable())
0539: return;
0540:
0541: String cont = ClipboardUtils
0542: .getClipboardStringContent();
0543: if (cont == null || cont.length() == 0)
0544: return;
0545:
0546: String indent = DocumentUtils
0547: .getSpacesAtBeginning(DocumentUtils
0548: .getTextOfLineAtPosition_onlyUpToPos(
0549: doc, textPane
0550: .getCaretPosition()));
0551:
0552: StringBuilder sb = new StringBuilder();
0553: for (String argi : cont.split(",")) {
0554: argi = argi.trim();
0555: if (argi.length() == 0)
0556: continue;
0557: if (argi.endsWith(")")) {
0558: argi = argi.substring(0, argi.length() - 1);
0559: if (argi.length() == 0)
0560: continue;
0561: }
0562:
0563: if (!argi.startsWith("final"))
0564: sb.append("final ");
0565: sb.append("" + argi + ";\n" + indent);
0566: }
0567:
0568: getDocument().insertString(sb.toString().trim(),
0569: textPane.getCaretPosition());
0570:
0571: }
0572: }, "InsertClassMembDeclForArgs",
0573: Accelerators.insertClassMembDeclForArgs,
0574: JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
0575:
0576: this .registerKeyboardAction(new ActionListener() {
0577: public void actionPerformed(ActionEvent ae) {
0578: FileItem fi = getActualDisplayedFile();
0579: if (fi != null) {
0580: int line = DocumentUtils.getLineNumber(doc,
0581: textPane.getCaretPosition());
0582: LineMessage mess = LineMessagesManager
0583: .getInstance().getMessageBeforeLine(
0584: fi.getJavaName(), line);
0585: if (mess != null) {
0586: MessagesTable.getInstance()
0587: .jumpToSourceForMessage(mess);
0588: }
0589: }
0590: }
0591: }, "JumpToPreviousMessage", Accelerators.jumpToPreviousMessage,
0592: JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
0593:
0594: this .registerKeyboardAction(new ActionListener() {
0595: public void actionPerformed(ActionEvent ae) {
0596: FileItem fi = getActualDisplayedFile();
0597: if (fi != null) {
0598: LineMessage mess = LineMessagesManager
0599: .getInstance().getFirstMessageLine(
0600: fi.getJavaName());
0601: if (mess != null) {
0602: MessagesTable.getInstance()
0603: .jumpToSourceForMessage(mess);
0604: }
0605: }
0606: }
0607: }, "Jump to first message", Accelerators.jumpToFirstMessage,
0608: JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
0609:
0610: this .registerKeyboardAction(new ActionListener() {
0611: public void actionPerformed(ActionEvent ae) {
0612: FileItem fi = getActualDisplayedFile();
0613: if (fi != null) {
0614: LineMessage mess = LineMessagesManager
0615: .getInstance().getLastMessageLine(
0616: fi.getJavaName());
0617: if (mess != null) {
0618: MessagesTable.getInstance()
0619: .jumpToSourceForMessage(mess);
0620: }
0621: }
0622: }
0623: }, "Jump to last message", Accelerators.jumpToLastMessage,
0624: JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
0625:
0626: this .registerKeyboardAction(new ActionListener() {
0627: public void actionPerformed(ActionEvent ae) {
0628: jumpToNextMethodOrField(textPane.getCaretPosition());
0629: }
0630: }, "jumpToNextAttr", Accelerators.jumpToNextAttr,
0631: JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
0632:
0633: this .registerKeyboardAction(
0634: new ActionListener() {
0635: public void actionPerformed(ActionEvent ae) {
0636: jumpToPreviousMethodOrField(textPane
0637: .getCaretPosition());
0638: }
0639: }, "jumpToPrevAttr", Accelerators.jumpToPreviousAttr,
0640: JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
0641:
0642: this .registerKeyboardAction(new ActionListener() {
0643: public void actionPerformed(ActionEvent ae) {
0644: jumpManager.jumpToPrevious();
0645: }
0646: }, "previousJumpPos", Accelerators.previousJumpPos,
0647: JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
0648:
0649: this .registerKeyboardAction(new ActionListener() {
0650: public void actionPerformed(ActionEvent ae) {
0651: jumpManager.redoUndoedJump();
0652: }
0653: }, "nextJumpPos", Accelerators.nextJumpPos,
0654: JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
0655:
0656: this .registerKeyboardAction(new ActionListener() {
0657: public void actionPerformed(ActionEvent ae) {
0658: FileItem fi = getActualDisplayedFile();
0659: if (fi != null) {
0660: FileItem ns = (FileItem) fi.getNextSibling();
0661: if (ns != null) {
0662: // IF FOLDER=> dig TODO
0663: if (!ns.isDirectory()) {
0664: MainEditorFrame.instance
0665: .setSourceOrItemToEditOrView(ns,
0666: true);
0667: }
0668: } else {
0669: //go up until find and take parent's first sibling and dig
0670: // tree walk... TODO
0671: }
0672: }
0673: }
0674: }, "JumpToNextSrcInTree", Accelerators.jumpToNextSrc,
0675: JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
0676:
0677: this .registerKeyboardAction(new ActionListener() {
0678: public void actionPerformed(ActionEvent ae) {
0679: FileItem fi = getActualDisplayedFile();
0680: if (fi != null) {
0681: TreeNode ns = fi.getPreviousSibling();
0682: if (ns != null) {
0683: MainEditorFrame.instance
0684: .setSourceOrItemToEditOrView(
0685: (FileItem) ns, true);
0686: } else {
0687: //go up until find and take parent's first sibling
0688: // tree walk... TODO
0689: }
0690: }
0691: }
0692: }, "JumpToPrevSrcInTree", Accelerators.jumpToPreviousSrc,
0693: JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
0694:
0695: this .registerKeyboardAction(new ActionListener() {
0696: public void actionPerformed(ActionEvent ae) {
0697: completionManager.dotWithoutCompletion(editedFile, doc,
0698: textPane.getCaretPosition());
0699: }
0700: }, "Dot Without Completion", Accelerators.dotWithoutCompletion,
0701: JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
0702:
0703: this .registerKeyboardAction(new ActionListener() {
0704: public void actionPerformed(ActionEvent ae) {
0705: if (editedFile != null) {
0706: int[] lineCol = DocumentUtils.getLineColumnNumbers(
0707: doc, textPane.getCaretPosition());
0708: String tt = JOptionPane.showInputDialog(
0709: EditorPanel.this ,
0710: "Bookmark description (optional)",
0711: "Add a bookmark",
0712: JOptionPane.QUESTION_MESSAGE);
0713: if (tt == null)
0714: return;
0715: MainEditorFrame.instance.getActualProject()
0716: .addBookmark(
0717: new SourceBookmark(editedFile
0718: .getJavaName(), lineCol[0],
0719: lineCol[1], tt));
0720: }
0721:
0722: }
0723: }, "AddBookmark", Accelerators.addBookmark,
0724: JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
0725: }
0726:
0727: @Override
0728: public void updateUI() {
0729: super .updateUI();
0730: if (this .doc != null) {
0731: doc.setOrUpdateStyles();
0732: this .codeStyler.userInsertOccured(0, 0);
0733:
0734: setSelectedTab(this .getActualDisplayedFile(), true);
0735: }
0736: }
0737:
0738: /** Called on load and on save. */
0739: public void resetTotalActiveTime() {
0740: codeStyler.resetTotalActiveTime();
0741: // ok...
0742: allChangedFilesHistory.clear();
0743: }
0744:
0745: public long getTotalActiveTime() {
0746: return codeStyler.getTotalActiveTime();
0747: }
0748:
0749: public int getNumberOfChangedFiles() {
0750: return changedFiles.size();
0751: }
0752:
0753: public EditorDocument getDocument() {
0754: return doc;
0755: }
0756:
0757: public JTextPane getTextPane() {
0758: return this .textPane;
0759: }
0760:
0761: public JScrollPane getScrollPane() {
0762: return this .editorScrollPane;
0763: }
0764:
0765: public JavaCodeStyler getCodeStyler() {
0766: return codeStyler;
0767: }
0768:
0769: /** null if none.
0770: */
0771: @edu.umd.cs.findbugs.annotations.CheckForNull
0772: public FileItem getActualDisplayedFile() {
0773: return editedFile;
0774: }
0775:
0776: public void saveChangedFiles() {
0777: saveChangedFiles(true);
0778: }
0779:
0780: /** Must be called at the end, before each compile operation, ...
0781: */
0782: public void saveChangedFiles(boolean notify) {
0783: // set the edited source content in the SourceFile object
0784: // maybe resolve the conflicts if external changed (SVN, SourceSafe, ...)
0785: storeTemporary_ActualEditedSource();
0786:
0787: // save all changed files on the disk (all that have been edited so far)
0788: MainEditorFrame.debugOut("Saving " + changedFiles.size()
0789: + " changed files");
0790:
0791: boolean hasErrors = false;
0792: for (SourceFile sf : changedFiles) {
0793: try {
0794: sf.saveContentToFile();
0795: } catch (Exception e) {
0796: System.out.println("Cannot save " + sf + ": "
0797: + e.getMessage());
0798: MainEditorFrame.instance
0799: .writeInternalErrorLine("Cannot save " + sf
0800: + ": " + e.getMessage());
0801: e.printStackTrace();
0802: hasErrors = true;
0803: }
0804: }
0805:
0806: // remove the edit flags up to the roots (only for directories)
0807: if (!hasErrors) {
0808: for (SourceFile sf : changedFiles) {
0809: while (!sf.isRoot()) {
0810: sf = (SourceFile) sf.getParent(); // can only be a dir
0811: sf.setBeingEdited(false);
0812: }
0813: }
0814: }
0815:
0816: MainEditorFrame.instance.sourcesTreePanel.updateTree();
0817:
0818: if (notify && changedFiles.size() > 0) {
0819: notifyChangedFilesSaveListener(changedFiles);
0820: }
0821:
0822: // no changed files anymore !
0823: changedFiles.clear();
0824:
0825: // OK, tell the gc now that there may be some memory free !
0826: //no, too slow... System.gc();
0827: }
0828:
0829: /** Added only once !
0830: */
0831: public void addChangedFilesSaveListener(ChangedFilesSaveListener cli) {
0832: synchronized (changedFilesSaveListeners) {
0833: if (!changedFilesSaveListeners.contains(cli)) // only once !!
0834: {
0835: changedFilesSaveListeners.add(cli);
0836: }
0837: }
0838: }
0839:
0840: public void removeChangedFilesSaveListener(
0841: ChangedFilesSaveListener cli) {
0842: synchronized (changedFilesSaveListeners) {
0843: changedFilesSaveListeners.remove(cli);
0844: }
0845: }
0846:
0847: private void notifyChangedFilesSaveListener(Set<SourceFile> changed) {
0848: // copy because cleared in the caller...
0849: HashSet<SourceFile> cfc = new HashSet<SourceFile>(changed);
0850:
0851: ChangedFilesSaveListener[] list = null;
0852: synchronized (changedFilesSaveListeners) {
0853: list = changedFilesSaveListeners
0854: .toArray(new ChangedFilesSaveListener[changedFilesSaveListeners
0855: .size()]);
0856: }
0857:
0858: for (ChangedFilesSaveListener cli : list) {
0859: cli.filesSaved(cfc);
0860: }
0861:
0862: }
0863:
0864: public void selectLine(int line) {
0865: selectLinePart(line, -1, -1);
0866: }
0867:
0868: /** Selects the document part, from start up to the given end position.
0869: * Scrolls to start and fetch focus.
0870: * Robust !
0871: */
0872: public void selectDocumentPart(int lineS, int colS, int lineE,
0873: int colE) {
0874: if (lineS < 0)
0875: return;
0876: int startP = DocumentUtils.getDocPositionFor(doc, lineS, colS);
0877: int endP = DocumentUtils.getDocPositionFor(doc, lineE, colE);
0878: textPane.setSelectionStart(startP);
0879:
0880: if (endP < startP) {
0881: System.out.println("Problem: " + startP + " up to " + endP);
0882: endP = Math.min(startP + 1, doc.getLength());
0883: }
0884:
0885: textPane.setSelectionEnd(endP); // misplacement detected thanks fb, dead store above
0886:
0887: scrollPosMiddle(startP, false);
0888: textPane.requestFocus(); // to make the selection visible
0889: }
0890:
0891: /** Selects up to the end of the line.
0892: */
0893: public void selectLinePart(int line, int column) {
0894: selectLinePart(line, column, -1);
0895: }
0896:
0897: /** Makes a selection, scrolls and fetch focus (so one can directly press delete or replace the selection)
0898: Called from the search tab.
0899: @param fromColumn if positive non null, the line will be selected from that column (1=first)
0900: @param selectionLength if >0 is used as sel length
0901: @deprecated NOT ROBUST, length is not good for multiline hits, use the method selectDocumentPart with line, col instead.
0902: */
0903: @Deprecated
0904: public void selectLinePart(int line, int fromColumn,
0905: int selectionLength) {
0906: if (line >= 0) {
0907: Element elem = doc.getRootElements()[0].getElement(line);
0908: if (elem == null) {
0909: System.out.println("elem null for line " + line);
0910: return;
0911: }
0912: int start = elem.getStartOffset();
0913: int end = elem.getEndOffset() - 1; // without the return at end!
0914:
0915: if (fromColumn > 0) {
0916: int nstart = start + fromColumn;
0917: if (nstart < elem.getEndOffset()) // ensures something is selected !
0918: {
0919: start = nstart;
0920: }
0921: }
0922:
0923: if (selectionLength > 0 && fromColumn >= 0) {
0924: if (start + selectionLength <= this .doc.getLength()) {
0925: end = start + selectionLength;
0926: }
0927: } else {
0928: // reduce spaces before and after !
0929: // TODO
0930: }
0931:
0932: textPane.setSelectionStart(start);
0933: textPane.setSelectionEnd(end);
0934:
0935: scrollPosMiddle(start, false);
0936: textPane.requestFocus(); // to make the selection visible
0937: }
0938: }
0939:
0940: /** From a parsed compiler or search output.
0941: Offers several selections, absolute (exact) if found, line, ...
0942: */
0943: public void selectItem(CompilerOutputItem item) {
0944: selectLinePart(item.lineStart, item.columnStart,
0945: item.itemLength);
0946: }
0947:
0948: /** @param select if true, selects the line where pos is.
0949: * Also called from the syntaxTreePanel
0950: * TODO: sometimes bad behaviour when at the doc end...
0951: */
0952: public void scrollPosMiddle(int pos, boolean select) {
0953: DocumentUtils.scrollToMiddle(textPane, pos); //editorScrollPane, this.getHeight()/3);
0954: int line = DocumentUtils.getLineNumber(this .doc, pos);
0955: if (select) {
0956: this .selectLine(line);
0957: }
0958:
0959: this .jumpManager.rememberPositionInEditor(line);
0960: }
0961:
0962: /** Stores the edited content in the file object (not on disk).
0963: * Must be called before search ops and changing tab.
0964: */
0965: public void storeTemporary_ActualEditedSource() {
0966: if (editedFile != null) {
0967: int[] lineCol = DocumentUtils.getLineColumnNumbers(doc,
0968: this .textPane.getCaretPosition());
0969: editedFile.setCaretPositionToRemember(lineCol[0],
0970: lineCol[1]);
0971:
0972: if (editedFile instanceof SourceFile) {
0973: // save
0974: SourceFile sf = (SourceFile) editedFile;
0975: if (sf.isEditable()) // was look
0976: {
0977: try {
0978: boolean allowExtChanges = false;
0979: if (sf
0980: .hasFileContentChangedOnDiskSinceLastGetContent()) {
0981: MainEditorFrame.instance.outputPanels.tideOutputPanel.doc
0982: .appendLine("Sourcefile "
0983: + sf.getJavaName()
0984: + " changed on disk during edition.");
0985:
0986: int rep = JOptionPane
0987: .showConfirmDialog(
0988: MainEditorFrame.instance,
0989: "Sourcefile "
0990: + sf.getJavaName()
0991: + " changed on disk since last tIDE's read."
0992: + "\nTake the (newest) version from disk and cancel changes made in tIDE ?",
0993: "Content changed on disk",
0994: JOptionPane.YES_NO_CANCEL_OPTION);
0995:
0996: if (rep == JOptionPane.YES_OPTION) {
0997: this .replaceContentWithContentOnDisk();
0998: //return;
0999: } else // no or cancel changes made in tIDE
1000: {
1001: allowExtChanges = true;
1002: sf.cancelExternalChanges();
1003: System.out
1004: .println("Cancelling ext changes, keeping tIDE's version of "
1005: + sf.getJavaName());
1006: }
1007: }
1008:
1009: sf.setContentFromEditor(
1010: this .textPane.getText(),
1011: allowExtChanges); // don't allow ext changes
1012: } catch (Exception e) {
1013: JOptionPane.showMessageDialog(null,
1014: "Can't save " + sf.getJavaName()
1015: + ":\n\n " + e.getMessage(),
1016: "tIDE store error",
1017: JOptionPane.ERROR_MESSAGE);
1018: }
1019: }
1020: }
1021: }
1022: }
1023:
1024: private volatile long lastEdit = 0;
1025:
1026: /** Set a E in the icon, repaint the tree (once, at first change)
1027: * and manages a syntax reparse later, after some pause.
1028: */
1029: public void setHasBeenEdited() {
1030: lastEdit = System.currentTimeMillis();
1031: if (editedFile != null && editedFile instanceof SourceFile) {
1032: SourceFile sf = (SourceFile) editedFile;
1033: if (!sf.isBeingEdited()) // only toggle once
1034: {
1035: sf.setBeingEdited(true);
1036: MainEditorFrame.instance.sourcesTreePanel.updateTree();
1037: changedFiles.add(sf);
1038: if (!allChangedFilesHistory.contains(sf)) {
1039: allChangedFilesHistory.add(sf);
1040: }
1041: }
1042: }
1043:
1044: reparseSyntaxLaterWhenSilent();
1045: }
1046:
1047: // used to limit to one
1048: private volatile Thread parserDelayer = null;
1049: private volatile InterruptableTask parserTask = null;
1050: private volatile InterruptableTask parserTask2 = null;
1051:
1052: /** Reparse after next second of user type inactivity.
1053: */
1054: public synchronized void reparseSyntaxLaterWhenSilent() {
1055: if (this .editedFile == null)
1056: return;
1057: if (parserDelayer != null) {
1058: //MainEditorFrame.debugOut(""+this.getActualDisplayedFile()+": already parsing");
1059: return;
1060: }
1061:
1062: final FileItem editedFileToReparse = editedFile;
1063: parserDelayer = new Thread(new Runnable() {
1064: public void run() {
1065: try {
1066: // wait one second
1067: while (System.currentTimeMillis() - lastEdit < 1000L) {
1068: try {
1069: Thread.sleep(500);
1070: } catch (Exception e) {
1071: }
1072: }
1073:
1074: // the file may have changed => abort!
1075: if (editedFileToReparse != editedFile)
1076: return;
1077:
1078: // again starts a new thread
1079:
1080: if (MainEditorFrame.useNewAST) {
1081: parserTask2 = MainEditorFrame.instance.syntaxTreePanel
1082: .setSource2(editedFile, getText());
1083: } else {
1084: parserTask = MainEditorFrame.instance.syntaxTreePanel
1085: .setSource(editedFile, getText());
1086: }
1087: //System.out.println("reparseSyntaxLaterWhenSilent:: set source done.");
1088: } finally // was missing [April2007]
1089: {
1090: parserDelayer = null;
1091: }
1092: }
1093: });
1094: parserDelayer.setName("parserDelayer");
1095: parserDelayer.setPriority(Thread.NORM_PRIORITY - 1);
1096: parserDelayer.start();
1097: }
1098:
1099: /** Set the whole text,
1100: disable the filter, so that this inserted text doesn't appear as to be "typed".
1101: */
1102: private void setTextContent(String txt) {
1103: txt = txt.replace("\r", ""); // IMPORTANT !
1104:
1105: doc.setDocumentFilter(null);
1106:
1107: doc.setText(txt);
1108:
1109: doc.setDocumentFilter(editorDocumentFilter);
1110:
1111: this .codeStyler.documentContentFullyChanged(txt.length());
1112: }
1113:
1114: /** User for automated text insert.
1115: */
1116: public void writeTextAtCaret(String text) {
1117: this .doc.insertString(text, this .textPane.getCaretPosition());
1118: }
1119:
1120: /** Should be called after having reload from CVS, SVN, of refreshed from filesystem.
1121: * The files must have been saved before sync. to empty their cached content.
1122: * If the file is no more existing, it must be removed !
1123: */
1124: public void replaceContentWithContentOnDisk() {
1125: System.out.println("Replace content for " + editedFile);
1126: if (editedFile == null)
1127: return;
1128:
1129: // enforce reload.
1130: editedFile.deleteCachedContent();
1131: try {
1132: this .setTextContent(editedFile.getContent());
1133: } catch (Exception e) {
1134: // todo:::
1135: // File not existing:
1136: e.printStackTrace();
1137: }
1138: }
1139:
1140: /** WARNING: This don't select the tree item !
1141: * displays/add the file in the editor tabs.
1142: * If the file is already the one shown => does nothing.
1143: */
1144: void setSourceFileToEdit(final FileItem src) {
1145: linePanel.setSourceFileToEdit(src);
1146:
1147: if (src != null && src instanceof SourceFile) {
1148: SourceFile sf = (SourceFile) src;
1149:
1150: if (src.isEditable()
1151: && editedFile == src
1152: && sf
1153: .hasFileContentChangedOnDiskSinceLastGetContent()) {
1154: // the same (editable) source => ask to replace !
1155:
1156: MainEditorFrame.instance.outputPanels.tideOutputPanel.doc
1157: .appendLine("Sourcefile " + sf.getJavaName()
1158: + " changed on disk during edition.");
1159:
1160: int rep = JOptionPane
1161: .showConfirmDialog(
1162: MainEditorFrame.instance,
1163: "Sourcefile "
1164: + sf.getJavaName()
1165: + " changed on disk since last tIDE's read."
1166: + "\nTake the (newest) version from disk and cancel changes made in tIDE ?",
1167: "Content changed on disk",
1168: JOptionPane.YES_NO_CANCEL_OPTION);
1169: if (rep == JOptionPane.YES_OPTION) {
1170: this .replaceContentWithContentOnDisk(); // ONLY BECAUSE src==editedFile
1171: return;
1172: } else // no, cancel ext changes and use tide's version
1173: {
1174: System.out
1175: .println("CANCEL ext changes and keep tIDE version of "
1176: + sf.getJavaName());
1177: sf.cancelExternalChanges();
1178: //return;
1179: }
1180: }
1181: }
1182:
1183: if (editedFile == src) {
1184: // just jump eventually
1185: MainEditorFrame.instance.syntaxTreePanel
1186: .selectMethodOrField(); // not see in the dependency ### selectMethodOrField
1187: return; // nothing to do.
1188: }
1189: //codeStyler.userInsertWillOccur(0, -1);
1190:
1191: // also store actual position
1192: storeTemporary_ActualEditedSource();
1193:
1194: this .completionManager.closeOpenedDialogs();
1195:
1196: editedFile = src;
1197:
1198: if (editedFile == null || !editedFile.hasTextRepresentation()) {
1199: editedFile = null; // null
1200: textPane.setEditable(false);
1201: doc.getAndRemoveUndoManager();
1202: setTextContent("no edited file");
1203: setSelectedTab(null, true);
1204: return;
1205: }
1206:
1207: try {
1208: textPane.setEditable(src.isEditable());
1209: doc.getAndRemoveUndoManager(); // just to avoid recording as undo the big operation below
1210: String content = src.getContent(); // eventually decompiles...
1211: setTextContent(content); // maybe temporary content
1212:
1213: if (src instanceof SourceFile) {
1214: SourceFile ssf = (SourceFile) src;
1215: if (ssf.undoManager == null)
1216: ssf.undoManager = new UndoManager();
1217: doc.reinstallUndoManager(ssf.undoManager);
1218: }
1219:
1220: try {
1221: int capo = DocumentUtils.getDocPositionFor(doc, src
1222: .getCaretLinePosition(), src
1223: .getCaretColumnPosition());
1224: if (capo <= doc.getLength() && capo >= 0) {
1225: textPane.setCaretPosition(capo);
1226: this .scrollPosMiddle(capo, false);
1227: } else {
1228: textPane.setCaretPosition(0); // let view the top !
1229: }
1230: } catch (Exception ignored) {
1231: Basics.ignore(ignored);
1232: }
1233:
1234: setSelectedTab(editedFile, true);
1235:
1236: // if not souce file, this resets the tree to null
1237:
1238: if (MainEditorFrame.useNewAST) {
1239: MainEditorFrame.instance.syntaxTreePanel.setSource2(
1240: src, content);
1241: } else {
1242: MainEditorFrame.instance.syntaxTreePanel.setSource(src,
1243: content); //OLD
1244: }
1245:
1246: } catch (Exception e) {
1247: e.printStackTrace(); // TODO
1248: }
1249:
1250: // immediate, without waiting, for the whole document
1251: //restyle(0);
1252: }
1253:
1254: /** @return the whole actual text content.
1255: */
1256: private String getText() {
1257: return doc.getText();
1258: //NO NO NO: textPane.getText();
1259: // AAAAAAHHH textPane.getText() gives different content!
1260: }
1261:
1262: //maybe null
1263: String getSelectedOrWordAtCaret() {
1264: if ((textPane.getSelectionEnd() - textPane.getSelectionStart()) > 0) {
1265: return textPane.getSelectedText();
1266: } else {
1267: // detect the word at the cursor
1268: return DocumentUtils.getWordAt(doc, textPane
1269: .getCaretPosition());
1270: }
1271: }
1272:
1273: public LinePanel getLinePanel() {
1274: return linePanel;
1275: }
1276:
1277: public StatusBar getStatusBar() {
1278: return statusBar;
1279: }
1280:
1281: /* The UI manager and components gives UIColor, this create a pure Color object.
1282: *
1283: private static Color createPureColor(Color c)
1284: {
1285: return new Color(c.getRed(), c.getGreen(), c.getBlue());
1286: }*/
1287:
1288: /** Called when a source file has been deleted in the tree.
1289: */
1290: public void removeTab(FileItem sf) {
1291: sourcesNavigationBar.removeTab(sf);
1292: }
1293:
1294: /** Selects the corresponding tab, create one if necessary, and limit the
1295: number of tabs according to the history bar width.
1296: */
1297: private void setSelectedTab(final FileItem sf,
1298: boolean limitToVisibleWidth) {
1299: sourcesNavigationBar.setSelectedTab(sf, limitToVisibleWidth);
1300: }
1301:
1302: /** Is used to store the IDE state.
1303: */
1304: public List<String> getTabsJavaNames() {
1305: return sourcesNavigationBar.getTabsJavaNames();
1306: }
1307:
1308: /** Called when reloading the project.
1309: this just recreate the tabs, no selection is made.
1310: */
1311: public void recreateTabsFromNames(java.util.List<String> javaNames) {
1312: sourcesNavigationBar.recreateTabsFromNames(javaNames);
1313: }
1314:
1315: public void removeAllTabs() {
1316: sourcesNavigationBar.removeAllTabs();
1317: }
1318:
1319: /** adds the missing import in the current displayed source
1320: * @param name is for example "java.util.*;"
1321: */
1322: public void addMissingImport(String name) {
1323:
1324: // insert after the first package statement
1325: int pos = doc.search("package ", 0, false); // TODO: ignore if in comment...
1326:
1327: if (this .getActualDisplayedFile() != null
1328: && this .getActualDisplayedFile().getPackageName()
1329: .length() == 0) {
1330: // [Sep2007]: repairs a bug. In the unnamed scope, no real package statement is present.
1331: pos = -1;
1332: }
1333:
1334: if (pos == -1) {
1335: // no package statement => insert at beginning.
1336: pos = 0;
1337: } else {
1338: // from enter after the package line
1339: pos = doc.search("\n", pos, false);
1340: if (pos == -1) {
1341: pos = doc.getLength();
1342: } else {
1343: pos++;
1344: }
1345: }
1346: doc.insertString("\r\nimport " + name, pos);
1347: }
1348:
1349: private void jumpToNextMethodOrField(int fromPos) {
1350: final int[] lineCol = DocumentUtils.getLineColumnNumbers(
1351: this .doc, fromPos);
1352: ParserTreeNode sTreeNode = MainEditorFrame.instance.syntaxTreePanel
1353: .getSimplifiedTreeElementForSourceLocation(
1354: lineCol[0] + 1, lineCol[1] + 1, editedFile);
1355: if (sTreeNode != null) {
1356: ParserTreeNode found = MainEditorFrame.instance.syntaxTreePanel
1357: .selectNextTo(sTreeNode);
1358: if (found != null) {
1359: int pos = MainEditorFrame.instance.syntaxTreePanel
1360: .selectTextForNode(found, textPane, false);
1361: if (pos >= 0) {
1362: textPane.setCaretPosition(pos);
1363: }
1364:
1365: }
1366: }
1367: }
1368:
1369: private void jumpToPreviousMethodOrField(int fromPos) {
1370: final int[] lineCol = DocumentUtils.getLineColumnNumbers(
1371: this .doc, fromPos);
1372: ParserTreeNode sTreeNode = MainEditorFrame.instance.syntaxTreePanel
1373: .getSimplifiedTreeElementForSourceLocation(
1374: lineCol[0] + 1, lineCol[1] + 1, editedFile);
1375: if (sTreeNode != null) {
1376: ParserTreeNode found = MainEditorFrame.instance.syntaxTreePanel
1377: .selectPreviousTo(sTreeNode);
1378: if (found != null) {
1379: int pos = MainEditorFrame.instance.syntaxTreePanel
1380: .selectTextForNode(found, textPane, false);
1381: if (pos >= 0) {
1382: textPane.setCaretPosition(pos);
1383: }
1384: }
1385: }
1386: }
1387:
1388: /** source editor's popup (TODO: if alt pressed, show only wizard code,
1389: * if ctrl, only jump, ... )
1390: * otherwise, show all items.
1391: */
1392: private void showPopup(final MouseEvent me) {
1393: final JPopupMenu popup = new JPopupMenu();
1394:
1395: final boolean isAnyKeyModifierPressed = me.isShiftDown()
1396: || me.isAltDown() || me.isControlDown()
1397: || me.isAltGraphDown();
1398:
1399: if (!isAnyKeyModifierPressed) {
1400: // Copy-paste
1401: //
1402:
1403: final String sel = getSelectedOrWordAtCaret();
1404: if (sel != null) // [Nov2007] only offer if apply...
1405: {
1406:
1407: JMenuItem copy = new JMenuItem("Copy");
1408: copy.setAccelerator(Accelerators.copy);
1409: popup.add(copy);
1410: copy.addActionListener(new ActionListener() {
1411: public void actionPerformed(ActionEvent ae) {
1412:
1413: if (sel != null) {
1414: ClipboardUtils.copyToClipboard(sel);
1415: }
1416: }
1417: });
1418:
1419: popup.addSeparator();
1420: }
1421:
1422: JMenuItem paste = new JMenuItem("Paste");
1423: paste.setAccelerator(Accelerators.paste);
1424: if (textPane.isEditable()) {
1425:
1426: popup.add(paste);
1427:
1428: paste.addActionListener(new ActionListener() {
1429: public void actionPerformed(ActionEvent ae) {
1430: if (!textPane.isEditable())
1431: return;
1432:
1433: String txt = ClipboardUtils
1434: .getClipboardStringContent();
1435: if (txt != null && txt.length() > 0) {
1436: // TODO: insert at cursor OR replace selection
1437: doc.insertString(txt, textPane
1438: .getCaretPosition());
1439: }
1440: }
1441: });
1442:
1443: if (ClipboardUtils.getInstance().getHistory().size() > 0) {
1444: JMenu pasteHistory = new JMenu("Paste history ");
1445: popup.add(pasteHistory);
1446: for (final String p : ClipboardUtils.getInstance()
1447: .getHistory()) {
1448: JMenuItem pasteH = new JMenuItem(""
1449: + StringUtils
1450: .shortFormForDisplay(p, 70));
1451: pasteHistory.add(pasteH);
1452: pasteH.addActionListener(new ActionListener() {
1453: public void actionPerformed(ActionEvent ae) {
1454: doc.insertString(p, textPane
1455: .getCaretPosition());
1456: }
1457: });
1458: }
1459: }
1460: }
1461:
1462: popup.addSeparator();
1463:
1464: }
1465:
1466: // Semantic, browse, jump and code wizards
1467: //
1468:
1469: // the menu is accessed through the context menu (right click) and maybe at another location
1470: // than the caret => use the clicked point position to detect the work (if no selection)
1471: final int clickedPos = textPane.viewToModel(me.getPoint());
1472: //System.out.println("Clicked in doc at pos "+clickedPos);
1473:
1474: String selectedText = getSelectedOrWordAtCaret();
1475:
1476: // if(selectedText==null) selectedText ="";
1477:
1478: final String fSelectedText = selectedText;
1479:
1480: final int[] lineCol = DocumentUtils.getLineColumnNumbers(
1481: this .doc, clickedPos);
1482: //final int col = DocumentUtils.getColumnNumber(this.doc, clickedPos);
1483: final int wordEnd = DocumentUtils.getWordEndPosition(this .doc,
1484: clickedPos);
1485:
1486: List<JMenuItem> missingImports = new ArrayList<JMenuItem>();
1487: List<JMenuItem> codeWizards = new ArrayList<JMenuItem>();
1488: List<JMenuItem> browseActions = new ArrayList<JMenuItem>(); // jump to...
1489: List<JMenuItem> docActions = new ArrayList<JMenuItem>();
1490:
1491: // Lazy, jumps only of possible...
1492: ParserTreeNode sTreeNode = MainEditorFrame.instance.syntaxTreePanel
1493: .selectSimplifiedTreeElementForSourceLocation(
1494: lineCol[0] + 1, lineCol[1] + 1, editedFile);
1495:
1496: if (sTreeNode != null) {
1497: // To create getter/setter, copy name, switch enum, ...
1498: for (Action a : ActionsForNode
1499: .generateActionsForNode(sTreeNode)) {
1500: JMenuItem mi = new JMenuItem(a);
1501: mi.setIcon(Icons.sharedWiz);
1502: codeWizards.add(mi);
1503: }
1504: }
1505:
1506: ParsedID pid = null;
1507: IDChain resolver = null;
1508: FileItem resolverType = null;
1509: SingleClassLoader scl = SingleClassLoader
1510: .createSingleClassLoader();
1511: try {
1512: pid = SyntaxUtils.getJavaIdentifierBefore(doc, wordEnd,
1513: null);
1514: resolver = new IDChain(pid, editedFile, doc, wordEnd,
1515: pid.followingItem, scl);
1516: if (resolver.isValid()) {
1517: if (resolver.getLastChainComponent() != null) {
1518: IDChainElement idc = resolver
1519: .getLastChainComponent();
1520: if (idc.resolvedType_ != null
1521: && idc.resolvedType_.fitem != null) {
1522: resolverType = idc.resolvedType_.fitem;
1523: } else if (idc.getTypeMaybeResolved() != null) {
1524: resolverType = completionManager.locateType(
1525: editedFile, idc.getTypeMaybeResolved(),
1526: false);
1527: }
1528: }
1529: } else {
1530: // todo: sort relevance (use frequency, length, ...)
1531: final List<FileItem> missingImp = completionManager
1532: .locateEventualMissingImport(editedFile,
1533: resolver.getLastChainComponent()
1534: .getName());
1535: if (missingImp != null && missingImp.size() > 0) {
1536: for (final FileItem fi : missingImp) {
1537: final JMenuItem admi = new JMenuItem(
1538: "Add missing import "
1539: + fi.getJavaName(),
1540: Icons.sharedWiz);
1541: admi
1542: .setToolTipText("Press SHIFT to import the whole package");
1543: missingImports.add(admi);
1544: admi.addActionListener(new ActionListener() {
1545: public void actionPerformed(ActionEvent ae) {
1546: if ((ae.getModifiers() & ActionEvent.SHIFT_MASK) == ActionEvent.SHIFT_MASK) {
1547: addMissingImport(StringUtils
1548: .removeAfterLastIncluded(fi
1549: .getJavaName(), ".")
1550: + ".*;");
1551: } else {
1552: addMissingImport(fi.getJavaName()
1553: + ";");
1554: }
1555: }
1556: });
1557: }
1558: }
1559:
1560: }
1561: } catch (Exception ignored) {
1562: MainEditorFrame.debugOut(ignored); //debug
1563: }
1564:
1565: final FileItem fresolverType = resolverType;
1566:
1567: if (resolverType != null) {
1568: // todo: only if "implements" or new preceeds "implements a, b, c" OR after "new ActionListener() {"
1569: try {
1570: final Class cla = scl.loadClass(resolverType
1571: .getJavaName());
1572:
1573: if (cla != null) {
1574: if (textPane.isEditable()
1575: && tide.classsyntax.ClassUtils
1576: .hasAbstractMethods(cla)) {
1577: JMenuItem implComp = new JMenuItem(
1578: "Implement abstract methods of "
1579: + SyntaxUtils
1580: .getJavaNameSimpleIfLongForView(fresolverType
1581: .getJavaName()),
1582: Icons.sharedWiz);
1583: codeWizards.add(implComp);
1584: implComp
1585: .addActionListener(new ActionListener() {
1586: public void actionPerformed(
1587: ActionEvent ae) {
1588: // find next "{" and go !
1589: int posOpenBrace = doc.search(
1590: "{", clickedPos, false);
1591: if (posOpenBrace == -1)
1592: posOpenBrace = doc
1593: .getLength() - 1;
1594:
1595: //int[] lc = DocumentUtils.getLineColumnNumbers(doc, posOpenBrace);
1596: String indent = DocumentUtils
1597: .getSpacesAtBeginning(DocumentUtils
1598: .getTextOfLineAtPosition_onlyUpToPos(
1599: doc,
1600: posOpenBrace));
1601: indent += " ";
1602:
1603: StringBuilder sb = new StringBuilder(
1604: "\r\n"
1605: + indent
1606: + "//tide generated:\r\n");
1607: for (Method m : cla
1608: .getMethods()) // only public, but also inherited.
1609: {
1610: // TODO: look for existing method names (=> don't add them)!
1611: if (!Modifier.isAbstract(m
1612: .getModifiers()))
1613: continue;
1614:
1615: if (fresolverType instanceof SourceFile) {
1616: DecompileManager
1617: .getInstance()
1618: .removeDecompForSources();
1619: }
1620:
1621: sb.append("\r\n" + indent);
1622:
1623: final List<String> names = AttributesParamNamesManager
1624: .getInstance()
1625: .getParameterNames(
1626: m);
1627: sb.append("public final ");
1628: String cn = ""
1629: + m
1630: .getGenericReturnType();
1631: //if(cn.startsWith("class ")) cn = cn.substring(6);
1632: sb
1633: .append(SyntaxUtils
1634: .makeAllJavaNamesSimpleInText(cn)); // [March2008]: made correct.
1635: // see for example java.lang.Override abstr.
1636:
1637: sb
1638: .append(" "
1639: + m
1640: .getName()
1641: + AttributesParamNamesManager
1642: .createArgumentListWithNames(
1643: m,
1644: names,
1645: null,
1646: true));
1647: sb.append(" {");
1648: if (m.getDeclaringClass() != cla) {
1649: sb
1650: .append(" // declared in "
1651: + m
1652: .getDeclaringClass());
1653: }
1654: sb.append("\r\n" + indent
1655: + "}\r\n");
1656: }
1657: doc.insertString(sb.toString(),
1658: posOpenBrace + 1);
1659:
1660: }
1661: });
1662: }
1663:
1664: if (textPane.isEditable() && cla.isEnum()) // switch(enumXX) {
1665: {
1666: JMenuItem enumComp = new JMenuItem(
1667: "Create text for switching enum "
1668: + resolverType.getJavaName(),
1669: Icons.sharedWiz);
1670: codeWizards.add(enumComp);
1671: enumComp
1672: .addActionListener(new ActionListener() {
1673: public void actionPerformed(
1674: ActionEvent ae) {
1675: int posOpenBrace = doc.search(
1676: "{", clickedPos, false);
1677: if (posOpenBrace == -1)
1678: posOpenBrace = doc
1679: .getLength() - 1;
1680: String indent = DocumentUtils
1681: .getSpacesAtBeginning(DocumentUtils
1682: .getTextOfLineAtPosition_onlyUpToPos(
1683: doc,
1684: posOpenBrace));
1685: indent += " ";
1686:
1687: StringBuilder sb = new StringBuilder(
1688: "\r\n//tide generated:");
1689: for (Object ec : cla
1690: .getEnumConstants()) {
1691: sb.append("\r\n" + indent
1692: + "case " + ec
1693: + ": break;");
1694: }
1695: //sb.append("\r\n"+indent+"default: break;");
1696: doc.insertString(sb.toString(),
1697: posOpenBrace + 1);
1698: }
1699: });
1700:
1701: }
1702:
1703: if (textPane.isEditable()
1704: && tide.classsyntax.ClassUtils
1705: .isIterable(cla)) {
1706: // Todo: not always nice, for example makes no sense to propose to iterate when clicking on "new List<Double>()"
1707:
1708: JMenuItem iter = new JMenuItem("Iterate over "
1709: + resolverType.getJavaName(),
1710: Icons.sharedWiz);
1711: final String name = resolver
1712: .getLastChainComponent().getName();
1713: String itName = "Object";
1714: //System.out.println("iterate over:: "+resolver);
1715: if (resolver.getLastChainComponent() != null) {
1716: if (resolver.getLastChainComponent()
1717: .getTypeParameters() != null) {
1718: itName = resolver
1719: .getLastChainComponent()
1720: .getTypeParameters();
1721: }
1722: }
1723: final String fitName = itName;
1724: iter.addActionListener(new ActionListener() {
1725: public void actionPerformed(ActionEvent ae) {
1726: int insPos = doc.search("\n",
1727: clickedPos, false);
1728:
1729: String indent = DocumentUtils
1730: .getSpacesAtBeginning(DocumentUtils
1731: .getTextOfLineAtPosition_onlyUpToPos(
1732: doc, clickedPos));
1733: //indent+=" ";
1734:
1735: //if(posOpenBrace==-1) posOpenBrace = doc.getLength()-1;
1736: StringBuilder sb = new StringBuilder(
1737: "\r\n" + indent
1738: + "//tide generated:");
1739: sb
1740: .append("\r\n"
1741: + indent
1742: + "for(final "
1743: + fitName
1744: + " "
1745: + SyntaxUtils
1746: .createVariableNameFromClassName(fitName)
1747: + " : " + name
1748: + ")\r\n" + indent
1749: + "{\r\n" + indent
1750: + "}");
1751: doc.insertString(sb.toString(), insPos);
1752: }
1753: });
1754: codeWizards.add(iter);
1755: }
1756:
1757: }
1758:
1759: } catch (Error e) {
1760: // occurs when classes can't be resolved...
1761: } catch (Exception e) {
1762: e.printStackTrace();
1763: }
1764: }
1765:
1766: try {
1767:
1768: if (resolver != null
1769: && resolver.getLastChainComponent() != null
1770: && resolver.getLastChainComponent().getArrayDepth() > 0) {
1771: final int ad = resolver.getLastChainComponent()
1772: .getArrayDepth();
1773: final String varName = resolver.getLastChainComponent()
1774: .getName();
1775: final String typeName = resolver
1776: .getLastChainComponent().getTypeMaybeResolved();
1777: final String sjn = SyntaxUtils
1778: .makeSingleJavaNameSimple(typeName);
1779:
1780: // fresolverType.getJavaName()
1781: JMenuItem iter = new JMenuItem("Iterate over array of "
1782: + sjn + " ", Icons.sharedWiz);
1783: codeWizards.add(iter);
1784: iter.addActionListener(new ActionListener() {
1785: public void actionPerformed(ActionEvent ae) {
1786: int insPos = doc
1787: .search("\n", clickedPos, false);
1788: String indent = DocumentUtils
1789: .getSpacesAtBeginning(DocumentUtils
1790: .getTextOfLineAtPosition_onlyUpToPos(
1791: doc, clickedPos));
1792: //indent+=" ";
1793:
1794: //if(posOpenBrace==-1) posOpenBrace = doc.getLength()-1;
1795: StringBuilder sb = new StringBuilder();//not really necessary... "\r\n"+indent+"//tide generated:");
1796:
1797: StringBuilder arrayPart = new StringBuilder();
1798: for (int i = 1; i < ad; i++) {
1799: arrayPart.append("[]");
1800: }
1801:
1802: sb
1803: .append("\r\n"
1804: + indent
1805: + "for(final "
1806: + sjn
1807: + arrayPart.toString()
1808: + " "
1809: + SyntaxUtils
1810: .createVariableNameFromClassName(sjn)
1811: + " : " + varName + ")\r\n"
1812: + indent + "{\r\n");
1813: sb.append(indent + " //\r\n");
1814: sb.append(indent + "}");
1815: doc.insertString(sb.toString(), insPos);
1816: }
1817: });
1818: }
1819:
1820: } catch (Exception e) {
1821: e.printStackTrace();
1822: }
1823:
1824: if (!missingImports.isEmpty()) {
1825: if (missingImports.size() > 2) {
1826: JMenu mimMenu = new JMenu("Add missing import");
1827: mimMenu.setIcon(Icons.sharedWiz);
1828: popup.add(mimMenu);
1829: for (JMenuItem mi : missingImports) {
1830: mimMenu.add(mi);
1831: }
1832: } else {
1833: for (final JMenuItem mi : missingImports) {
1834: popup.add(mi);
1835: }
1836: }
1837: popup.addSeparator();
1838: missingImports.clear();
1839: missingImports = null;
1840:
1841: }
1842:
1843: if (!codeWizards.isEmpty()) {
1844: if (codeWizards.size() > 2) {
1845: JMenu codeWizardsMenu = new JMenu("Code wizards");
1846: codeWizardsMenu.setIcon(Icons.sharedWiz);
1847: popup.add(codeWizardsMenu);
1848: for (JMenuItem mi : codeWizards) {
1849: codeWizardsMenu.add(mi);
1850: }
1851: } else {
1852: for (final JMenuItem mi : codeWizards) {
1853: popup.add(mi);
1854: }
1855: }
1856: popup.addSeparator();
1857: codeWizards.clear();
1858: codeWizards = null;
1859: }
1860:
1861: if (resolverType != null) {
1862: final File jdf = MainEditorFrame.instance
1863: .getActualProject().getJavaDocManager().getDocFile(
1864: resolverType.getJavaName(), false);
1865: if (jdf != null && jdf.exists()) {
1866: // Local javaDoc
1867: JMenuItem javaDoc = new JMenuItem(
1868: "View JavaDoc for "
1869: + SyntaxUtils
1870: .getJavaNameSimpleIfLongForView(resolverType
1871: .getJavaName()),
1872: SourcesTreeIcon.createSimpleLetterIcon("D"));
1873: docActions.add(javaDoc);
1874: javaDoc.addActionListener(new ActionListener() {
1875: public void actionPerformed(ActionEvent ae) {
1876: try {
1877: SysUtils.openBrowser(jdf.getAbsolutePath());
1878: } catch (Exception e) {
1879: MainEditorFrame.instance
1880: .displayErrorMessage(e,
1881: "Can't open browser for "
1882: + jdf);
1883: }
1884: }
1885: });
1886: }
1887: } else {
1888: /// [Feb2008]: inner classes
1889:
1890: String tn = null;
1891: try {
1892: tn = resolver.getLastChainComponent()
1893: .getTypeMaybeResolved();
1894: } catch (Exception ignore) {
1895: }
1896:
1897: //System.out.println("no resolverType, last elt: "+tn );
1898: if (tn != null) {
1899: final File jdf = MainEditorFrame.instance
1900: .getActualProject().getJavaDocManager()
1901: .getDocFile(tn, false);
1902: if (jdf != null && jdf.exists()) {
1903: // Local javaDoc
1904: JMenuItem javaDoc = new JMenuItem(
1905: "View JavaDoc for "
1906: + SyntaxUtils
1907: .getJavaNameSimpleIfLongForView(tn),
1908: SourcesTreeIcon.createSimpleLetterIcon("D"));
1909: docActions.add(javaDoc);
1910: javaDoc.addActionListener(new ActionListener() {
1911: public void actionPerformed(ActionEvent ae) {
1912: try {
1913: SysUtils.openBrowser(jdf
1914: .getAbsolutePath());
1915: } catch (Exception e) {
1916: MainEditorFrame.instance
1917: .displayErrorMessage(e,
1918: "Can't open browser for "
1919: + jdf);
1920: }
1921: }
1922: });
1923: }
1924: }
1925:
1926: }
1927:
1928: if (fSelectedText != null
1929: && Tips.langSpecRelPathsForKeyWordsMap
1930: .containsKey(fSelectedText)) {
1931: final String url = Tips.langSpecBaseURL
1932: + Tips.langSpecRelPathsForKeyWordsMap
1933: .get(fSelectedText);
1934: JMenuItem lsDoc = new JMenuItem("View lang spec for "
1935: + fSelectedText, SourcesTreeIcon
1936: .createSimpleLetterIcon("S"));
1937: docActions.add(lsDoc);
1938: lsDoc.addActionListener(new ActionListener() {
1939: public void actionPerformed(ActionEvent ae) {
1940: try {
1941: SysUtils.openBrowser(url);
1942: } catch (Exception e) {
1943: MainEditorFrame.instance.displayErrorMessage(e,
1944: "Can't open browser for " + url);
1945: }
1946: }
1947: });
1948: }
1949:
1950: //
1951:
1952: if (docActions.size() > 0) {
1953: for (JMenuItem mi : docActions) {
1954: popup.add(mi);
1955: }
1956: popup.addSeparator();
1957: }
1958:
1959: if (resolverType != null
1960: && resolverType != this .getActualDisplayedFile()) {
1961: JMenuItem jumpI = new JMenuItem("Class "
1962: + resolverType.getJavaName(),
1963: Icons.sharedRightArrow);
1964: browseActions.add(jumpI);
1965: jumpI.addActionListener(new ActionListener() {
1966: public void actionPerformed(ActionEvent ae) {
1967: MainEditorFrame.instance
1968: .setSourceOrItemToEditOrView(fresolverType,
1969: true);
1970: }
1971: });
1972: }
1973:
1974: if (resolver != null
1975: && resolver.getLastChainComponent() != null) {
1976: final IDChainElement methodElement = resolver
1977: .getLastChainComponent();
1978: final FileItem typeForField = resolver
1979: .getDeclaringClassForMethodOrField(methodElement);
1980: if (typeForField != null) {
1981:
1982: // [March2008]: removed the "Jump to" blabla
1983: if (resolver.getLastChainComponent().getKind() == ElementKind.Method) {
1984: JMenuItem jumpM = new JMenuItem("Method "
1985: + methodElement.getName() + " in "
1986: + typeForField.getJavaPartName(),
1987: Icons.sharedRightArrow);
1988: browseActions.add(jumpM);
1989: jumpM.addActionListener(new ActionListener() {
1990: public void actionPerformed(ActionEvent ae) {
1991: MainEditorFrame.instance.setMethodToView(
1992: typeForField, methodElement
1993: .getName());
1994: }
1995: });
1996: } else if (resolver.getLastChainComponent().getKind() == ElementKind.Field) {
1997: JMenuItem jumpM = new JMenuItem("Field "
1998: + methodElement.getName() + " in "
1999: + typeForField.getJavaPartName(),
2000: Icons.sharedRightArrow);
2001: browseActions.add(jumpM);
2002: jumpM.addActionListener(new ActionListener() {
2003: public void actionPerformed(ActionEvent ae) {
2004: MainEditorFrame.instance.setFieldToView(
2005: typeForField, methodElement
2006: .getName());
2007: }
2008: });
2009: } else // [March2008]: only shows the jump to the class if not already has a jump to the method or to the field.
2010: {
2011: // this code was above, before if()
2012: if (typeForField != resolverType
2013: && typeForField != this
2014: .getActualDisplayedFile()) {
2015: JMenuItem jumpI = new JMenuItem("Class "
2016: + typeForField.getJavaName(),
2017: Icons.sharedRightArrow);
2018: browseActions.add(jumpI);
2019: jumpI.addActionListener(new ActionListener() {
2020: public void actionPerformed(ActionEvent ae) {
2021: MainEditorFrame.instance
2022: .setSourceOrItemToEditOrView(
2023: typeForField, true);
2024: }
2025: });
2026: }
2027: }
2028:
2029: }
2030: }
2031:
2032: if (browseActions.size() > 0) {
2033: for (JMenuItem mi : browseActions) {
2034: popup.add(mi);
2035: }
2036: popup.addSeparator();
2037: browseActions = null;
2038: }
2039:
2040: // Search functions for selected text
2041: //
2042:
2043: final String ft = selectedText;
2044: char nextchar = doc.getCharAt(clickedPos);
2045:
2046: if ((ft != null && ft.equals("{")) || nextchar == '{') {
2047: JMenuItem searchF = new JMenuItem("Search ending \"}\"",
2048: Icons.sharedSearch);
2049: popup.add(searchF);
2050: popup.addSeparator();
2051: searchF.addActionListener(new ActionListener() {
2052: public void actionPerformed(ActionEvent ae) {
2053: int posEnd = DocumentUtils
2054: .getPositionOfCorrespondingClosingBrace(
2055: '{', '}', doc, clickedPos);
2056: if (posEnd >= 0) {
2057: scrollPosMiddle(posEnd, false);
2058: try {
2059: selectedWordTags.add(textPane
2060: .getHighlighter().addHighlight(
2061: posEnd, posEnd + 1,
2062: doc.autoSelectHighlighter));
2063: } catch (Exception e) {
2064: e.printStackTrace();
2065: }
2066:
2067: }
2068: }
2069: });
2070: }
2071: if ((ft != null && ft.equals("}")) || nextchar == '}') {
2072: JMenuItem searchF = new JMenuItem("Search opening \"{\"");
2073: popup.addSeparator();
2074: popup.add(searchF);
2075: searchF.addActionListener(new ActionListener() {
2076: public void actionPerformed(ActionEvent ae) {
2077: int posEnd = DocumentUtils
2078: .getPositionOfCorrespondingOpeningBrace(
2079: '{', '}', doc, clickedPos);
2080: if (posEnd >= 0) {
2081: scrollPosMiddle(posEnd, false);
2082: try {
2083: selectedWordTags.add(textPane
2084: .getHighlighter().addHighlight(
2085: posEnd, posEnd + 1,
2086: doc.autoSelectHighlighter));
2087: } catch (Exception e) {
2088: e.printStackTrace();
2089: }
2090: }
2091: }
2092: });
2093: }
2094:
2095: if (selectedText != null) {
2096: final String shortText = StringUtils.shortFormForDisplay(
2097: selectedText, 70);
2098: JMenuItem searchF = new JMenuItem("Search next \""
2099: + shortText + "\"", Icons.sharedSearch);
2100:
2101: // indicate CTRL+F to hint the user, but ALT+down does... NO NO
2102: searchF.setAccelerator(Accelerators.nextOccurence);
2103: popup.add(searchF);
2104: searchF.addActionListener(new ActionListener() {
2105: public void actionPerformed(ActionEvent ae) {
2106: searchPanel.searchNext_calledFromPopup(ft,
2107: clickedPos + 1);
2108: }
2109: });
2110:
2111: JMenuItem searchP = new JMenuItem("Search previous \""
2112: + shortText + "\"", Icons.sharedSearch);
2113: searchP.setAccelerator(Accelerators.previousOccurence);
2114: popup.add(searchP);
2115: searchP.addActionListener(new ActionListener() {
2116: public void actionPerformed(ActionEvent ae) {
2117: searchPanel.searchPrevious_calledFromPopup(ft,
2118: clickedPos);
2119: }
2120: });
2121:
2122: JMenuItem search1 = new JMenuItem("Search first \""
2123: + shortText + "\"", Icons.sharedSearch);
2124: search1.setAccelerator(Accelerators.searchFirstOcc);
2125: popup.add(search1);
2126: search1.addActionListener(new ActionListener() {
2127: public void actionPerformed(ActionEvent ae) {
2128: searchPanel.searchNext_calledFromPopup(ft, 0);
2129: }
2130: });
2131:
2132: JMenuItem searchG = new JMenuItem("Global search for \""
2133: + shortText + "\"", Icons.sharedSearch);
2134: searchG.setAccelerator(Accelerators.globalSearch);
2135: popup.addSeparator();
2136: popup.add(searchG);
2137: searchG.addActionListener(new ActionListener() {
2138: public void actionPerformed(ActionEvent ae) {
2139: MainEditorFrame.instance.globalProperties
2140: .setProperty("SearchTool_searchTF", ft);
2141: MainEditorFrame.instance.globalProperties
2142: .setBoolean("SearchTool_regex", false);
2143:
2144: new SearchTool(
2145: MainEditorFrame.instance.sourcesTreePanel
2146: .getTreeModel().getAllSourceFiles(
2147: false), false, null);
2148: }
2149: });
2150:
2151: final FileItem actualDisplayedFile = MainEditorFrame.instance.editorPanel
2152: .getActualDisplayedFile();
2153: if (actualDisplayedFile != null) {
2154: final String pn = actualDisplayedFile.getPackageName();
2155: JMenuItem searchGP = new JMenuItem("Search in package "
2156: + pn + " for \"" + shortText + "\"",
2157: Icons.sharedSearch);
2158: searchGP.setAccelerator(Accelerators.searchInPackage);
2159: //popup.addSeparator();
2160: popup.add(searchGP);
2161: searchGP.addActionListener(new ActionListener() {
2162: public void actionPerformed(ActionEvent ae) {
2163: MainEditorFrame.instance.globalProperties
2164: .setProperty("SearchTool_searchTF", ft);
2165: MainEditorFrame.instance.globalProperties
2166: .setBoolean("SearchTool_regex", false);
2167:
2168: if (actualDisplayedFile instanceof SourceFile) {
2169: List<SourceFile> all = new ArrayList<SourceFile>();
2170: MainEditorFrame.instance.sourcesTreePanel
2171: .getTreeModel()
2172: .getAllSourceFilesRecurse(
2173: (SourceFile) actualDisplayedFile
2174: .getParent(), all,
2175: false, false);
2176: new SearchTool(all, false,
2177: "Search in package " + pn + ".* ("
2178: + all.size() + " sources)");
2179: } else {
2180: List<LibFileItem> all = new ArrayList<LibFileItem>();
2181: MainEditorFrame.instance.librariesPanel
2182: .getTreeModel()
2183: .getAllFilesRecurse(
2184: all,
2185: (LibFileItem) actualDisplayedFile
2186: .getParent(), true);
2187: new SearchTool(all, false,
2188: "Search in package " + pn + ".* ("
2189: + all.size() + " classes)");
2190: }
2191: }
2192: });
2193: }
2194:
2195: if (!isAnyKeyModifierPressed) {
2196: JMenuItem selAll = new JMenuItem("Highlight all \""
2197: + shortText + "\"");
2198: selAll
2199: .setAccelerator(Accelerators.highlightAllOccurences);
2200: popup.addSeparator();
2201: popup.add(selAll);
2202:
2203: selAll.addActionListener(new ActionListener() {
2204: public void actionPerformed(ActionEvent ae) {
2205: autoSelectWords(ft);
2206: }
2207: });
2208: }
2209:
2210: // first locate exact matching and if not found, inexacts...
2211: // TODO: try without $, with "." before
2212: final List<SourceFile> hits = MainEditorFrame.instance.sourcesTreePanel
2213: .getTreeModel().getAllSourceFilesContainingName(
2214: selectedText, true, true);
2215: final List<LibFileItem> hitsLib = MainEditorFrame.instance.librariesPanel
2216: .getTreeModel().getAllFilesContainingName(
2217: selectedText, true, true, true);
2218:
2219: if (hits.isEmpty() && hitsLib.isEmpty()) {
2220: hits.addAll(MainEditorFrame.instance.sourcesTreePanel
2221: .getTreeModel()
2222: .getAllSourceFilesContainingName(selectedText,
2223: true, false));
2224: if (hits.isEmpty()) {
2225: hits
2226: .addAll(MainEditorFrame.instance.sourcesTreePanel
2227: .getTreeModel()
2228: .getAllSourceFilesContainingName(
2229: selectedText, false, false));
2230: }
2231:
2232: if (hits.isEmpty()) {
2233: hitsLib
2234: .addAll(MainEditorFrame.instance.librariesPanel
2235: .getTreeModel()
2236: .getAllFilesContainingName(
2237: selectedText, true, false,
2238: true));
2239: }
2240:
2241: if (hitsLib.isEmpty()) {
2242: hitsLib
2243: .addAll(MainEditorFrame.instance.librariesPanel
2244: .getTreeModel()
2245: .getAllFilesContainingName(
2246: selectedText, false, false,
2247: true));
2248: }
2249: }
2250:
2251: if (!hits.isEmpty() || !hitsLib.isEmpty()) {
2252: JMenu jumpMenu = new JMenu("Jump to similar class");
2253: jumpMenu.setIcon(Icons.sharedRightArrow);
2254: popup.addSeparator();
2255: popup.add(jumpMenu);
2256:
2257: int n = 0;
2258: fl: for (final SourceFile sfi : hits) {
2259: n++;
2260: JMenuItem jumpI = new JMenuItem(sfi.getJavaName());
2261: jumpMenu.add(jumpI);
2262: jumpI.addActionListener(new ActionListener() {
2263: public void actionPerformed(ActionEvent ae) {
2264: MainEditorFrame.instance
2265: .setSourceOrItemToEditOrView(sfi,
2266: true);
2267: }
2268: });
2269:
2270: if (n > 20) {
2271: if (hits.size() - n > 0) {
2272: jumpMenu.add("... " + (hits.size() - n)
2273: + " more project sources... ");
2274: break fl;
2275: }
2276: }
2277: }
2278:
2279: if (hits.size() > 0 || hitsLib.size() > 0) {
2280: popup.add(jumpMenu);
2281: }
2282:
2283: n = 0;
2284: fl: for (int i = 0; i < hitsLib.size(); i++) {
2285: n++;
2286: final LibFileItem li = hitsLib.get(i);
2287: JMenuItem jumpI = new JMenuItem(li.getJavaName());
2288: jumpMenu.add(jumpI);
2289: jumpI.addActionListener(new ActionListener() {
2290: public void actionPerformed(ActionEvent ae) {
2291: MainEditorFrame.instance
2292: .setSourceOrItemToEditOrView(li,
2293: true);
2294: }
2295: });
2296:
2297: if (n > 20 && (hitsLib.size() - n) > 0) {
2298: jumpMenu.add("... " + (hitsLib.size() - n)
2299: + " more entries in libs... ");
2300: break fl;
2301: }
2302: }
2303:
2304: JMenuItem seea = new JMenuItem("Browse all ...");
2305: jumpMenu.add(seea);
2306: seea.addActionListener(new ActionListener() {
2307: public void actionPerformed(ActionEvent ae) {
2308: // TODO: browse (open CTRL+T and set the search text).
2309: //MainEditorFrame.instance.setSourceOrItemToEditOrView(li, true);
2310: completionManager.controlTPressed(editedFile,
2311: doc, textPane.getCaretPosition(),
2312: false, ft);
2313: }
2314: });
2315:
2316: }
2317: }
2318:
2319: JMenu syntaxTreeMenu = new JMenu("Syntax tree");
2320: popup.add(syntaxTreeMenu);
2321:
2322: JMenuItem locateTypeInTree = new JMenuItem(
2323: "Locate enclosing type in syntax tree");
2324: syntaxTreeMenu.add(locateTypeInTree);
2325: locateTypeInTree.addActionListener(new ActionListener() {
2326: public void actionPerformed(ActionEvent ae) {
2327: MainEditorFrame.instance.syntaxTreePanel
2328: .selectTreeTypeForSourceLocation(
2329: lineCol[0] + 1, lineCol[1] + 1,
2330: editedFile);
2331: }
2332: });
2333:
2334: /*JMenuItem locateSTree = new JMenuItem("Locate node in syntax tree");
2335: syntaxTreeMenu.add(locateSTree);
2336: locateSTree.addActionListener(new ActionListener()
2337: {
2338: public void actionPerformed(ActionEvent ae)
2339: {
2340: MainEditorFrame.instance.syntaxTreePanel.selectSimplifiedTreeElementForSourceLocation( lineCol[0]+1, lineCol[1]+1, editedFile );
2341: }
2342: });*/
2343:
2344: // NOT NECESARY: [June2007]
2345: // right click on an item will show it when the raw tree is included !!!
2346: /*if(MainEditorFrame.instance.includeRAWCCTree.isSelected())
2347: {
2348: JMenuItem locateRAWTree = new JMenuItem("Locate in RAW syntax tree");
2349: syntaxTreeMenu.addSeparator ();
2350: syntaxTreeMenu.add(locateRAWTree);
2351: locateRAWTree.addActionListener(new ActionListener()
2352: {
2353: public void actionPerformed(ActionEvent ae)
2354: {
2355: MainEditorFrame.instance.syntaxTreePanel.selectRAWTreeElementForSourceLocation( lineCol[0]+1, lineCol[1]+1, editedFile );
2356: }
2357: });
2358:
2359: JMenuItem locateRAWTreeBefore = new JMenuItem("Locate previous in RAW syntax tree");
2360: syntaxTreeMenu.add(locateRAWTreeBefore);
2361: locateRAWTreeBefore.addActionListener(new ActionListener()
2362: {
2363: public void actionPerformed(ActionEvent ae)
2364: {
2365: MainEditorFrame.instance.syntaxTreePanel.selectRAWTreeElementJustBeforeForSourceLocation( lineCol[0]+1, lineCol[1]+1, editedFile );
2366: }
2367: });
2368:
2369: JMenuItem locateRAWTreeAfter = new JMenuItem("Locate next in RAW syntax tree");
2370: syntaxTreeMenu.add(locateRAWTreeAfter);
2371: locateRAWTreeAfter.addActionListener(new ActionListener()
2372: {
2373: public void actionPerformed(ActionEvent ae)
2374: {
2375: MainEditorFrame.instance.syntaxTreePanel.selectRAWTreeElementJustAfterForSourceLocation( lineCol[0]+1, lineCol[1]+1, editedFile );
2376: }
2377: });
2378: }*/
2379:
2380: if (!isAnyKeyModifierPressed) {
2381: popup.addSeparator();
2382: // no. Is already present in the linePanel:
2383: // SharedUtils.addBookmarksPopup( popup );
2384:
2385: if (editedFile != null) {
2386: JMenuItem addBookmark = new JMenuItem(
2387: "Add a bookmark here", Icons.sharedPlus);
2388: addBookmark.setAccelerator(Accelerators.addBookmark);
2389: popup.add(addBookmark);
2390: addBookmark.addActionListener(new ActionListener() {
2391: public void actionPerformed(ActionEvent ae) {
2392: if (editedFile != null) {
2393: String tt = JOptionPane.showInputDialog(
2394: EditorPanel.this ,
2395: "Bookmark description (optional)",
2396: "Add a bookmark",
2397: JOptionPane.QUESTION_MESSAGE);
2398: if (tt == null)
2399: return;
2400: MainEditorFrame.instance
2401: .getActualProject()
2402: .addBookmark(
2403: new SourceBookmark(
2404: editedFile
2405: .getJavaName(),
2406: lineCol[0],
2407: lineCol[1], tt));
2408: }
2409: }
2410: });
2411: }
2412:
2413: if (resolver != null && MainEditorFrame.debug) {
2414: JMenu debug = new JMenu("ID before");
2415: popup.addSeparator();
2416: popup.add(debug);
2417: debug.add(resolver.toStringHTML());
2418: }
2419: }
2420:
2421: popup.show(this .textPane, me.getX(), me.getY());
2422: }
2423:
2424: /** Called from output's popup to directly search some selected text in the outputs in the editor.
2425: * Very useful for findbugs ouputs...
2426: */
2427: public void searchFirst(String occ) {
2428: this .searchPanel.setVisible(true);
2429: searchPanel.searchNext_calledFromPopup(occ, 0);
2430: }
2431:
2432: final List<Object> selectedWordTags = new ArrayList<Object>();
2433:
2434: private void clearAutoSelectedWords() {
2435: //System.out.println("Clearing "+selectedWordTags.size()+" sel tags");
2436: for (Object o : selectedWordTags) {
2437: textPane.getHighlighter().removeHighlight(o);
2438: }
2439: selectedWordTags.clear();
2440: searchPanel.setInfoText("");
2441: }
2442:
2443: private void autoSelectWords(String w) {
2444: try {
2445: int[] visibleBounds = DocumentUtils.getVisibleDocPosBounds(
2446: textPane, this .editorScrollPane);
2447:
2448: // the quick part first:
2449: String part = textPane.getText(visibleBounds[0],
2450: visibleBounds[1] - visibleBounds[0]);
2451: int pos = -1;
2452: while ((pos = part.indexOf(w, pos + 1)) >= 0) {
2453: selectedWordTags.add(textPane.getHighlighter()
2454: .addHighlight(pos + visibleBounds[0],
2455: pos + visibleBounds[0] + w.length(),
2456: doc.autoSelectHighlighter));
2457: }
2458:
2459: // the whole document now (ignoring already performed part)
2460:
2461: part = getText();
2462: //textPane.getText(0, doc.getLength()); /// AAAAAAHHH textPane.getText() gives different content!
2463: pos = -1;
2464: while ((pos = part.indexOf(w, pos + 1)) >= 0) {
2465: if (pos <= visibleBounds[0] || pos >= visibleBounds[1]) {
2466: selectedWordTags.add(textPane.getHighlighter()
2467: .addHighlight(pos, pos + w.length(),
2468: doc.autoSelectHighlighter));
2469: }
2470: }
2471:
2472: searchPanel.setInfoText("" + selectedWordTags.size()
2473: + " occurence"
2474: + (selectedWordTags.size() == 1 ? "" : "s")
2475: + " of \""
2476: + StringUtils.shortFormForDisplay(w, 25) + "\"");
2477:
2478: if (!searchPanel.isVisible() && selectedWordTags.size() > 0) {
2479: searchPanel.setVisible(true);
2480: }
2481: } catch (Exception e) {
2482: e.printStackTrace();
2483: }
2484: }
2485:
2486: public void mouseClickedInEditor(MouseEvent e) {
2487: // already in the SourceEditorKit:
2488: // 1 click: position caret
2489: // 2 clicks: select word (own modified action)
2490: // 3 clicks: select line
2491: //
2492: // 4 clicks and more: select block, skip 4-clicks "{"
2493:
2494: int ccount = e.getClickCount();
2495:
2496: if (ccount == 1) {
2497: clearAutoSelectedWords();
2498: } else if (ccount == 2) {
2499: // The selection of the word on double click is performed in the SourceEditorKit.SelectWord action !
2500: // here we just highlight
2501:
2502: int sl = textPane.getSelectionEnd()
2503: - textPane.getSelectionStart();
2504: if (sl > 0 && sl < 1000) {
2505: String sel = textPane.getSelectedText();
2506: // highlight them all !
2507: autoSelectWords(sel);
2508: }
2509: } else if (ccount > 3) {
2510:
2511: try {
2512: int start = DocumentUtils.getPreviousOpeningPosition(
2513: doc, '{', '}', textPane.getCaretPosition(),
2514: ccount - 4);
2515: if (start < 0)
2516: start = 0;
2517: textPane.setSelectionStart(start);
2518:
2519: int end = DocumentUtils
2520: .getPositionOfCorrespondingClosingBrace('{',
2521: '}', doc, start) + 1;
2522: if (end <= 0)
2523: end = doc.getLength();
2524: textPane.setSelectionEnd(end);
2525: } catch (Exception ex) {
2526: System.out
2527: .println("Warning: "
2528: + ccount
2529: + " clicks in EditorPanel has caused an error: "
2530: + ex.getMessage());
2531: //ex.printStackTrace();
2532: }
2533: }
2534:
2535: // [Jan2007] Track every new insert points, but only if clicked.
2536: if (editedFile != null) {
2537: int[] lineCol = DocumentUtils.getLineColumnNumbers(doc,
2538: this .textPane.getCaretPosition());
2539: editedFile.setCaretPositionToRemember(lineCol[0],
2540: lineCol[1]);
2541: }
2542: }
2543:
2544: /** called from tree, he knows when something changed !
2545: */
2546: public void updateHistoryIcons() {
2547: sourcesNavigationBar.updateHistoryIcons();
2548:
2549: }
2550:
2551: }
|