0001: /*
0002: * Sun Public License Notice
0003: *
0004: * The contents of this file are subject to the Sun Public License
0005: * Version 1.0 (the "License"). You may not use this file except in
0006: * compliance with the License. A copy of the License is available at
0007: * http://www.sun.com/
0008: *
0009: * The Original Code is NetBeans. The Initial Developer of the Original
0010: * Code is Sun Microsystems, Inc. Portions Copyright 1997-2000 Sun
0011: * Microsystems, Inc. All Rights Reserved.
0012: */
0013:
0014: package org.netbeans.editor;
0015:
0016: import java.awt.Container;
0017: import java.awt.Frame;
0018: import java.awt.event.ActionEvent;
0019: import java.awt.event.KeyEvent;
0020:
0021: import javax.swing.Action;
0022: import javax.swing.KeyStroke;
0023: import javax.swing.SwingUtilities;
0024: import javax.swing.plaf.TextUI;
0025: import javax.swing.text.BadLocationException;
0026: import javax.swing.text.Caret;
0027: import javax.swing.text.Document;
0028: import javax.swing.text.EditorKit;
0029: import javax.swing.text.JTextComponent;
0030:
0031: /**
0032: * Various useful editor functions. Some of the methods have the same names and
0033: * signatures like in javax.swing.Utilities but there is also many other useful
0034: * methods. All the methods are static so there's no reason to instantiate
0035: * Utilities.
0036: *
0037: * All the methods working with the document rely on that it is locked against
0038: * modification so they don't acquire document read/write lock by themselves to
0039: * guarantee the full thread safety of the execution. It's the user's task to
0040: * lock the document appropriately before using methods described here.
0041: *
0042: * Most of the methods require org.netbeans.editor.BaseDocument instance not
0043: * just the javax.swing.text.Document. The reason for that is to mark that the
0044: * methods work on BaseDocument instances only, not on generic documents. To
0045: * convert the Document to BaseDocument the simple conversion
0046: * (BaseDocument)target.getDocument() can be done or the method
0047: * getDocument(target) can be called. There are also other conversion methods
0048: * like getEditorUI(), getKit() or getKitClass().
0049: *
0050: * @author Miloslav Metelka
0051: * @version 0.10
0052: */
0053:
0054: public class Utilities {
0055:
0056: private static final String WRONG_POSITION_LOCALE = "wrong_position"; // NOI18N
0057:
0058: /** Switch the case to capital letters. Used in changeCase() */
0059: public static final int CASE_UPPER = 0;
0060:
0061: /** Switch the case to small letters. Used in changeCase() */
0062: public static final int CASE_LOWER = 1;
0063:
0064: /** Switch the case to reverse. Used in changeCase() */
0065: public static final int CASE_SWITCH = 2;
0066:
0067: private Utilities() {
0068: // instantiation has no sense
0069: }
0070:
0071: /**
0072: * Get the starting position of the row.
0073: *
0074: * @param c
0075: * text component to operate on
0076: * @param offset
0077: * position in document where to start searching
0078: * @return position of the start of the row or -1 for invalid position
0079: */
0080: public static int getRowStart(JTextComponent c, int offset)
0081: throws BadLocationException {
0082: return getRowStart((BaseDocument) c.getDocument(), offset, 0);
0083: }
0084:
0085: /**
0086: * Get the starting position of the row.
0087: *
0088: * @param doc
0089: * document to operate on
0090: * @param offset
0091: * position in document where to start searching
0092: * @return position of the start of the row or -1 for invalid position
0093: */
0094: public static int getRowStart(BaseDocument doc, int offset)
0095: throws BadLocationException {
0096: return getRowStart(doc, offset, 0);
0097: }
0098:
0099: /**
0100: * Get the starting position of the row while providing relative count of
0101: * row how the given position should be shifted. This is the most efficient
0102: * way how to move by lines in the document based on some position. There is
0103: * no similair getRowEnd() method that would have shifting parameter.
0104: *
0105: * @param doc
0106: * document to operate on
0107: * @param offset
0108: * position in document where to start searching
0109: * @param lineShift
0110: * shift the given offset forward/back relatively by some amount
0111: * of lines
0112: * @return position of the start of the row or -1 for invalid position
0113: */
0114: public static int getRowStart(BaseDocument doc, int offset,
0115: int lineShift) throws BadLocationException {
0116: if (lineShift != 0) {
0117: return doc.op.getBOLRelLine(offset, lineShift);
0118: } else { // no shift
0119: return doc.op.getBOL(offset);
0120: }
0121: }
0122:
0123: /**
0124: * Get the first non-white character on the line. The
0125: * document.isWhitespace() is used to test whether the particular character
0126: * is white space or not.
0127: *
0128: * @param doc
0129: * document to operate on
0130: * @param offset
0131: * position in document anywhere on the line
0132: * @return position of the first non-white char on the line or -1 if there's
0133: * no non-white character on that line.
0134: */
0135: public static int getRowFirstNonWhite(BaseDocument doc, int offset)
0136: throws BadLocationException {
0137: return getFirstNonWhiteFwd(doc, doc.op.getBOL(offset), doc.op
0138: .getEOL(offset));
0139: }
0140:
0141: /**
0142: * Get the last non-white character on the line. The document.isWhitespace()
0143: * is used to test whether the particular character is white space or not.
0144: *
0145: * @param doc
0146: * document to operate on
0147: * @param offset
0148: * position in document anywhere on the line
0149: * @return position of the last non-white char on the line or -1 if there's
0150: * no non-white character on that line.
0151: */
0152: public static int getRowLastNonWhite(BaseDocument doc, int offset)
0153: throws BadLocationException {
0154: return getFirstNonWhiteBwd(doc, doc.op.getEOL(offset), doc.op
0155: .getBOL(offset));
0156: }
0157:
0158: /**
0159: * Get indentation on the current line. If this line is white then return
0160: * -1.
0161: *
0162: * @param doc
0163: * document to operate on
0164: * @param offset
0165: * position in document anywhere on the line
0166: * @return indentation or -1 if the line is white
0167: */
0168: public static int getRowIndent(BaseDocument doc, int offset)
0169: throws BadLocationException {
0170: offset = getRowFirstNonWhite(doc, offset);
0171: if (offset == -1) {
0172: return -1;
0173: }
0174: return doc.op.getVisColFromPos(offset);
0175: }
0176:
0177: /**
0178: * Get indentation on the current line. If this line is white then go either
0179: * up or down an return indentation of the first non-white row. The
0180: * <tt>getRowFirstNonWhite()</tt> is used to find the indentation on
0181: * particular line.
0182: *
0183: * @param doc
0184: * document to operate on
0185: * @param offset
0186: * position in document anywhere on the line
0187: * @param downDir
0188: * if this flag is set to true then if the row is white then the
0189: * indentation of the next first non-white row is returned. If
0190: * it's false then the indentation of the previous first
0191: * non-white row is returned.
0192: * @return indentation or -1 if there's no non-white line in the specified
0193: * direction
0194: */
0195: public static int getRowIndent(BaseDocument doc, int offset,
0196: boolean downDir) throws BadLocationException {
0197: int p = getRowFirstNonWhite(doc, offset);
0198: if (p == -1) {
0199: p = getFirstNonWhiteRow(doc, offset, downDir);
0200: if (p == -1) {
0201: return -1; // non-white line not found
0202: }
0203: p = getRowFirstNonWhite(doc, p);
0204: if (p == -1) {
0205: return -1; // non-white line not found
0206: }
0207: }
0208: return doc.op.getVisColFromPos(p);
0209: }
0210:
0211: /**
0212: * Get the end position of the row right before the new-line character.
0213: *
0214: * @param c
0215: * text component to operate on
0216: * @param offset
0217: * position in document where to start searching
0218: * @param relLine
0219: * shift offset forward/back by some amount of lines
0220: * @return position of the end of the row or -1 for invalid position
0221: */
0222: public static int getRowEnd(JTextComponent c, int offset)
0223: throws BadLocationException {
0224: return getRowEnd((BaseDocument) c.getDocument(), offset);
0225: }
0226:
0227: public static int getRowEnd(BaseDocument doc, int offset)
0228: throws BadLocationException {
0229: return doc.op.getEOL(offset);
0230: }
0231:
0232: /**
0233: * Get the position that is one line above and visually at some x-coordinate
0234: * value.
0235: *
0236: * @param doc
0237: * document to operate on
0238: * @param offset
0239: * position in document from which the current line is determined
0240: * @param x
0241: * float x-coordinate value
0242: * @return position of the character that is at the one line above at the
0243: * required x-coordinate value
0244: */
0245: public static int getPositionAbove(JTextComponent c, int offset,
0246: int x) throws BadLocationException {
0247: BaseDocument doc = (BaseDocument) c.getDocument();
0248: BaseTextUI ui = (BaseTextUI) c.getUI();
0249: offset = ui.viewToModel(c, x, ui.getYFromPos(offset)
0250: - ui.getEditorUI().getLineHeight());
0251: return offset;
0252: }
0253:
0254: /**
0255: * Get the position that is one line above and visually at some x-coordinate
0256: * value.
0257: *
0258: * @param c
0259: * text component to operate on
0260: * @param offset
0261: * position in document from which the current line is determined
0262: * @param x
0263: * float x-coordinate value
0264: * @return position of the character that is at the one line above at the
0265: * required x-coordinate value
0266: */
0267: public static int getPositionBelow(JTextComponent c, int offset,
0268: int x) throws BadLocationException {
0269: BaseDocument doc = (BaseDocument) c.getDocument();
0270: BaseTextUI ui = (BaseTextUI) c.getUI();
0271: offset = ui.viewToModel(c, x, ui.getYFromPos(offset)
0272: + ui.getEditorUI().getLineHeight());
0273: return offset;
0274: }
0275:
0276: /**
0277: * Get start of the current word. If there are no more words till the
0278: * begining of the document, this method returns -1.
0279: *
0280: * @param c
0281: * text component to operate on
0282: * @param offset
0283: * position in document from which the current line is determined
0284: */
0285: public static int getWordStart(JTextComponent c, int offset)
0286: throws BadLocationException {
0287: return getWordStart((BaseDocument) c.getDocument(), offset);
0288: }
0289:
0290: public static int getWordStart(BaseDocument doc, int offset)
0291: throws BadLocationException {
0292: int docLen = doc.getLength();
0293: return doc.find(new FinderFactory.PreviousWordBwdFinder(doc,
0294: false, true), offset, 0);
0295: }
0296:
0297: public static int getWordEnd(JTextComponent c, int offset)
0298: throws BadLocationException {
0299: return getWordEnd((BaseDocument) c.getDocument(), offset);
0300: }
0301:
0302: public static int getWordEnd(BaseDocument doc, int offset)
0303: throws BadLocationException {
0304: return doc.find(new FinderFactory.NextWordFwdFinder(doc, false,
0305: true), offset, -1);
0306: }
0307:
0308: public static int getNextWord(JTextComponent c, int offset)
0309: throws BadLocationException {
0310: return getNextWord((BaseDocument) c.getDocument(), offset);
0311: }
0312:
0313: public static int getNextWord(BaseDocument doc, int offset)
0314: throws BadLocationException {
0315: Finder nextWordFinder = (Finder) doc
0316: .getProperty(SettingsNames.NEXT_WORD_FINDER);
0317: offset = doc.find(nextWordFinder, offset, -1);
0318: if (offset < 0) {
0319: offset = doc.getLength();
0320: }
0321: return offset;
0322: }
0323:
0324: public static int getPreviousWord(JTextComponent c, int offset)
0325: throws BadLocationException {
0326: return getPreviousWord((BaseDocument) c.getDocument(), offset);
0327: }
0328:
0329: public static int getPreviousWord(BaseDocument doc, int offset)
0330: throws BadLocationException {
0331: Finder prevWordFinder = (Finder) doc
0332: .getProperty(SettingsNames.PREVIOUS_WORD_FINDER);
0333: offset = doc.find(prevWordFinder, offset, 0);
0334: if (offset < 0) {
0335: offset = 0;
0336: }
0337: return offset;
0338: }
0339:
0340: /**
0341: * Get first white character in document in forward direction
0342: *
0343: * @param doc
0344: * document to operate on
0345: * @param offset
0346: * position in document where to start searching
0347: * @return position of the first white character or -1
0348: */
0349: public static int getFirstWhiteFwd(BaseDocument doc, int offset)
0350: throws BadLocationException {
0351: return getFirstWhiteFwd(doc, offset, -1);
0352: }
0353:
0354: /**
0355: * Get first white character in document in forward direction
0356: *
0357: * @param doc
0358: * document to operate on
0359: * @param offset
0360: * position in document where to start searching
0361: * @param limitPos
0362: * position in document (greater or equal than offset) where the
0363: * search will stop reporting unsuccessful search by returning -1
0364: * @return position of the first non-white character or -1
0365: */
0366: public static int getFirstWhiteFwd(BaseDocument doc, int offset,
0367: int limitPos) throws BadLocationException {
0368: return doc.find(new FinderFactory.WhiteFwdFinder(doc), offset,
0369: limitPos);
0370: }
0371:
0372: /**
0373: * Get first non-white character in document in forward direction
0374: *
0375: * @param doc
0376: * document to operate on
0377: * @param offset
0378: * position in document where to start searching
0379: * @return position of the first non-white character or -1
0380: */
0381: public static int getFirstNonWhiteFwd(BaseDocument doc, int offset)
0382: throws BadLocationException {
0383: return getFirstNonWhiteFwd(doc, offset, -1);
0384: }
0385:
0386: /**
0387: * Get first non-white character in document in forward direction
0388: *
0389: * @param doc
0390: * document to operate on
0391: * @param offset
0392: * position in document where to start searching
0393: * @param limitPos
0394: * position in document (greater or equal than offset) where the
0395: * search will stop reporting unsuccessful search by returning -1
0396: * @return position of the first non-white character or -1
0397: */
0398: public static int getFirstNonWhiteFwd(BaseDocument doc, int offset,
0399: int limitPos) throws BadLocationException {
0400: return doc.find(new FinderFactory.NonWhiteFwdFinder(doc),
0401: offset, limitPos);
0402: }
0403:
0404: /**
0405: * Get first white character in document in backward direction. The
0406: * character right before the character at position offset will be searched
0407: * as first.
0408: *
0409: * @param doc
0410: * document to operate on
0411: * @param offset
0412: * position in document where to start searching
0413: * @return position of the first white character or -1
0414: */
0415: public static int getFirstWhiteBwd(BaseDocument doc, int offset)
0416: throws BadLocationException {
0417: return getFirstWhiteBwd(doc, offset, 0);
0418: }
0419:
0420: /**
0421: * Get first white character in document in backward direction. The
0422: * character right before the character at position offset will be searched
0423: * as first.
0424: *
0425: * @param doc
0426: * document to operate on
0427: * @param offset
0428: * position in document where to start searching
0429: * @param limitPos
0430: * position in document (lower or equal than offset) where the
0431: * search will stop reporting unsuccessful search by returning -1
0432: * @return position of the first white character or -1
0433: */
0434: public static int getFirstWhiteBwd(BaseDocument doc, int offset,
0435: int limitPos) throws BadLocationException {
0436: return doc.find(new FinderFactory.WhiteBwdFinder(doc), offset,
0437: limitPos);
0438: }
0439:
0440: /**
0441: * Get first non-white character in document in backward direction. The
0442: * character right before the character at position offset will be searched
0443: * as first.
0444: *
0445: * @param doc
0446: * document to operate on
0447: * @param offset
0448: * position in document where to start searching
0449: * @return position of the first non-white character or -1
0450: */
0451: public static int getFirstNonWhiteBwd(BaseDocument doc, int offset)
0452: throws BadLocationException {
0453: return getFirstNonWhiteBwd(doc, offset, 0);
0454: }
0455:
0456: /**
0457: * Get first non-white character in document in backward direction. The
0458: * character right before the character at position offset will be searched
0459: * as first.
0460: *
0461: * @param doc
0462: * document to operate on
0463: * @param offset
0464: * position in document where to start searching
0465: * @param limitPos
0466: * position in document (lower or equal than offset) where the
0467: * search will stop reporting unsuccessful search by returning -1
0468: * @return position of the first non-white character or -1
0469: */
0470: public static int getFirstNonWhiteBwd(BaseDocument doc, int offset,
0471: int limitPos) throws BadLocationException {
0472: return doc.find(new FinderFactory.NonWhiteBwdFinder(doc),
0473: offset, limitPos);
0474: }
0475:
0476: /**
0477: * Return line offset (line number - 1) for some position in the document
0478: *
0479: * @param doc
0480: * document to operate on
0481: * @param offset
0482: * position in document where to start searching
0483: */
0484: public static int getLineOffset(BaseDocument doc, int offset)
0485: throws BadLocationException {
0486: return doc.op.getLine(offset);
0487: }
0488:
0489: /**
0490: * Return start offset of the line
0491: *
0492: * @param lineOffset
0493: * line offset starting from 0
0494: * @return start position of the line or -1 if lineOffset was invalid
0495: */
0496: public static int getRowStartFromLineOffset(BaseDocument doc,
0497: int lineOffset) {
0498: return doc.op.getBOLFromLine(lineOffset);
0499: }
0500:
0501: /**
0502: * Return visual column (with expanded tabs) on the line.
0503: *
0504: * @param doc
0505: * document to operate on
0506: * @param offset
0507: * position in document for which the visual column should be
0508: * found
0509: * @return visual column on the line determined by position
0510: */
0511: public static int getVisualColumn(BaseDocument doc, int offset)
0512: throws BadLocationException {
0513: return doc.op.getVisColFromPos(offset);
0514: }
0515:
0516: /**
0517: * @JMT custom method
0518: */
0519: public static boolean isEOL(BaseDocument doc, int offset)
0520: throws BadLocationException {
0521: return doc.op.isEOL(offset);
0522: }
0523:
0524: /**
0525: * Get the identifier around the given position or null if there's no
0526: * identifier
0527: *
0528: * @see getIdentifierBlock()
0529: */
0530: public static String getIdentifier(BaseDocument doc, int offset)
0531: throws BadLocationException {
0532: int[] blk = getIdentifierBlock(doc, offset);
0533: return (blk != null) ? doc.getText(blk[0], blk[1] - blk[0])
0534: : null;
0535: }
0536:
0537: /**
0538: * Get the identifier around the given position or null if there's no
0539: * identifier around the given position. The identifier must be accepted by
0540: * SyntaxSupport.isIdnetifier() otherwise null is returned.
0541: *
0542: * @param doc
0543: * document to work on
0544: * @param offset
0545: * position in document - usually the caret.getDot()
0546: * @return the block (starting and ending position) enclosing the identifier
0547: * or null if no identifier was found
0548: */
0549: public static int[] getIdentifierBlock(BaseDocument doc, int offset)
0550: throws BadLocationException {
0551: int[] ret = null;
0552: int idStart = getWordStart(doc, offset);
0553: if (idStart >= 0) {
0554: int idEnd = getWordEnd(doc, idStart);
0555: if (idEnd >= 0) {
0556: String id = doc.getText(idStart, idEnd - idStart);
0557: if (doc.getSyntaxSupport().isIdentifier(id)) {
0558: ret = new int[] { idStart, idEnd };
0559: } else { // not identifier by syntax support
0560: id = getWord(doc, offset); // try right at offset
0561: if (doc.getSyntaxSupport().isIdentifier(id)) {
0562: ret = new int[] { offset, offset + id.length() };
0563: }
0564: }
0565: }
0566: }
0567: return ret;
0568: }
0569:
0570: /**
0571: * Get the word around the given position .
0572: *
0573: * @param c
0574: * component to work with
0575: * @param offset
0576: * position in document - usually the caret.getDot()
0577: * @return the word.
0578: */
0579: public static String getWord(JTextComponent c, int offset)
0580: throws BadLocationException {
0581: String id = null;
0582: Document doc = c.getDocument();
0583: int idStart = javax.swing.text.Utilities
0584: .getWordStart(c, offset);
0585: if (idStart >= 0) {
0586: int idEnd = javax.swing.text.Utilities.getWordEnd(c,
0587: idStart);
0588: if (idEnd >= 0) {
0589: id = doc.getText(idStart, idEnd - idStart);
0590: if (id.trim().length() == 0) {
0591: int prevWordStart = javax.swing.text.Utilities
0592: .getPreviousWord(c, offset);
0593: if (offset == javax.swing.text.Utilities
0594: .getWordEnd(c, prevWordStart))
0595: id = doc.getText(prevWordStart, offset
0596: - prevWordStart);
0597: } else if ((id != null) && (id.length() != 0)
0598: && (id.indexOf(".") != -1)) {
0599: int index = offset - idStart;
0600: int begin = id.substring(0, index).lastIndexOf(".");
0601: begin = (begin == -1) ? 0 : begin + 1; // first index after
0602: // the dot, if
0603: // exists
0604: int end = id.indexOf(".", index);
0605: end = (end == -1) ? id.length() : end;
0606: id = id.substring(begin, end);
0607: }
0608: }
0609: }
0610: return id;
0611: }
0612:
0613: /**
0614: * Get the selection if there's any or get the identifier around the
0615: * position if there's no selection.
0616: *
0617: * @param c
0618: * component to work with
0619: * @param offset
0620: * position in document - usually the caret.getDot()
0621: * @return the block (starting and ending position) enclosing the identifier
0622: * or null if no identifier was found
0623: */
0624: public static int[] getSelectionOrIdentifierBlock(JTextComponent c,
0625: int offset) throws BadLocationException {
0626: BaseDocument doc = (BaseDocument) c.getDocument();
0627: Caret caret = c.getCaret();
0628: int[] ret;
0629: if (caret.isSelectionVisible()) {
0630: ret = new int[] { c.getSelectionStart(),
0631: c.getSelectionEnd() };
0632: } else {
0633: ret = getIdentifierBlock(doc, caret.getDot());
0634: }
0635: return ret;
0636: }
0637:
0638: /**
0639: * Get the selection or identifier at the current caret position
0640: *
0641: * @see getSelectionOrIdentifierBlock(JTextComponent, int)
0642: */
0643: public static int[] getSelectionOrIdentifierBlock(JTextComponent c) {
0644: try {
0645: return getSelectionOrIdentifierBlock(c, c.getCaret()
0646: .getDot());
0647: } catch (BadLocationException e) {
0648: return null;
0649: }
0650: }
0651:
0652: /**
0653: * Get the identifier before the given position (ending at given offset) or
0654: * null if there's no identifier
0655: */
0656: public static String getIdentifierBefore(BaseDocument doc,
0657: int offset) throws BadLocationException {
0658: int wordStart = getWordStart(doc, offset);
0659: if (wordStart != -1) {
0660: String word = new String(doc.getChars(wordStart, offset
0661: - wordStart), 0, offset - wordStart);
0662: if (doc.getSyntaxSupport().isIdentifier(word)) {
0663: return word;
0664: }
0665: }
0666: return null;
0667: }
0668:
0669: /**
0670: * Get the selection if there's any or get the identifier around the
0671: * position if there's no selection.
0672: */
0673: public static String getSelectionOrIdentifier(JTextComponent c,
0674: int offset) throws BadLocationException {
0675: Document doc = c.getDocument();
0676: Caret caret = c.getCaret();
0677: String ret;
0678: if (caret.isSelectionVisible()) {
0679: ret = c.getSelectedText();
0680: if (ret != null)
0681: return ret;
0682: }
0683: if (doc instanceof BaseDocument) {
0684: ret = getIdentifier((BaseDocument) doc, caret.getDot());
0685: } else {
0686: ret = getWord(c, offset);
0687: }
0688: return ret;
0689: }
0690:
0691: /** Get the selection or identifier at the current caret position */
0692: public static String getSelectionOrIdentifier(JTextComponent c) {
0693: try {
0694: return getSelectionOrIdentifier(c, c.getCaret().getDot());
0695: } catch (BadLocationException e) {
0696: return null;
0697: }
0698: }
0699:
0700: /**
0701: * Get the word at given position.
0702: */
0703: public static String getWord(BaseDocument doc, int offset)
0704: throws BadLocationException {
0705: int wordEnd = getWordEnd(doc, offset);
0706: if (wordEnd != -1) {
0707: return new String(doc.getChars(offset, wordEnd - offset),
0708: 0, wordEnd - offset);
0709: }
0710: return null;
0711: }
0712:
0713: /**
0714: * Change the case for specified part of document
0715: *
0716: * @param doc
0717: * document to operate on
0718: * @param offset
0719: * position in document determines the changed area begining
0720: * @param len
0721: * number of chars to change
0722: * @param type
0723: * either CASE_CAPITAL, CASE_SMALL or CASE_SWITCH
0724: */
0725: public static boolean changeCase(BaseDocument doc, int offset,
0726: int len, int type) throws BadLocationException {
0727: char[] orig = doc.getChars(offset, len);
0728: char[] changed = (char[]) orig.clone();
0729: for (int i = 0; i < orig.length; i++) {
0730: switch (type) {
0731: case CASE_UPPER:
0732: changed[i] = Character.toUpperCase(orig[i]);
0733: break;
0734: case CASE_LOWER:
0735: changed[i] = Character.toLowerCase(orig[i]);
0736: break;
0737: case CASE_SWITCH:
0738: if (Character.isUpperCase(orig[i])) {
0739: changed[i] = Character.toLowerCase(orig[i]);
0740: } else if (Character.isLowerCase(orig[i])) {
0741: changed[i] = Character.toUpperCase(orig[i]);
0742: }
0743: break;
0744: }
0745: }
0746: // check chars for difference and possibly change document
0747: for (int i = 0; i < orig.length; i++) {
0748: if (orig[i] != changed[i]) {
0749: doc.atomicLock();
0750: try {
0751: doc.remove(offset, orig.length);
0752: doc.insertString(offset, new String(changed), null);
0753: } finally {
0754: doc.atomicUnlock();
0755: }
0756: return true; // changed
0757: }
0758: }
0759: return false;
0760: }
0761:
0762: /**
0763: * Tests whether the line contains no characters except the ending new-line.
0764: *
0765: * @param doc
0766: * document to operate on
0767: * @param offset
0768: * position anywhere on the tested line
0769: * @return whether the line is empty or not
0770: */
0771: public static boolean isRowEmpty(BaseDocument doc, int offset)
0772: throws BadLocationException {
0773: return (doc.op.getBOL(offset) == doc.op.getEOL(offset));
0774: }
0775:
0776: public static int getFirstNonEmptyRow(BaseDocument doc, int offset,
0777: boolean downDir) throws BadLocationException {
0778: while (offset != -1 && isRowEmpty(doc, offset)) {
0779: offset = getRowStart(doc, offset, downDir ? +1 : -1);
0780: }
0781: return offset;
0782: }
0783:
0784: /**
0785: * Tests whether the line contains only whitespace characters.
0786: *
0787: * @param doc
0788: * document to operate on
0789: * @param offset
0790: * position anywhere on the tested line
0791: * @return whether the line is empty or not
0792: */
0793: public static boolean isRowWhite(BaseDocument doc, int offset)
0794: throws BadLocationException {
0795: offset = doc.find(new FinderFactory.NonWhiteFwdFinder(doc),
0796: doc.op.getBOL(offset), doc.op.getEOL(offset));
0797: return (offset == -1);
0798: }
0799:
0800: public static int getFirstNonWhiteRow(BaseDocument doc, int offset,
0801: boolean downDir) throws BadLocationException {
0802: if (isRowWhite(doc, offset)) {
0803: if (downDir) { // search down for non-white line
0804: offset = getFirstNonWhiteFwd(doc, offset);
0805: } else { // search up for non-white line
0806: offset = getFirstNonWhiteBwd(doc, offset);
0807: }
0808: }
0809: return offset;
0810: }
0811:
0812: /**
0813: * Reformat a block of code.
0814: *
0815: * @param doc
0816: * document to work with
0817: * @param startOffset
0818: * offset at which the formatting starts
0819: * @param endOffset
0820: * offset at which the formatting ends
0821: * @return length of the reformatted code
0822: */
0823: public static int reformat(BaseDocument doc, int startOffset,
0824: int endOffset) throws BadLocationException {
0825: return doc.getFormatter().reformat(doc, startOffset, endOffset);
0826: }
0827:
0828: /** Reformat the line around the given position. */
0829: public static void reformatLine(BaseDocument doc, int pos)
0830: throws BadLocationException {
0831: int lineStart = getRowStart(doc, pos);
0832: int lineEnd = getRowEnd(doc, pos);
0833: reformat(doc, lineStart, lineEnd);
0834: }
0835:
0836: /** Count of rows between these two positions */
0837: public static int getRowCount(BaseDocument doc, int startPos,
0838: int endPos) throws BadLocationException {
0839: if (startPos > endPos) {
0840: return 0;
0841: }
0842: return doc.op.getLine(endPos) - doc.op.getLine(startPos) + 1;
0843: }
0844:
0845: /** Get the total count of lines in the document */
0846: public static int getRowCount(BaseDocument doc) {
0847: return doc.op.getLineCount();
0848: }
0849:
0850: /**
0851: * @deprecated
0852: * @see Formatter.insertTabString()
0853: */
0854: public static String getTabInsertString(BaseDocument doc, int offset)
0855: throws BadLocationException {
0856: int col = getVisualColumn(doc, offset);
0857: Formatter f = doc.getFormatter();
0858: boolean expandTabs = f.expandTabs();
0859: if (expandTabs) {
0860: int spacesPerTab = f.getSpacesPerTab();
0861: int len = (col + spacesPerTab) / spacesPerTab
0862: * spacesPerTab - col;
0863: return new String(Analyzer.getSpacesBuffer(len), 0, len);
0864: } else { // insert pure tab
0865: return "\t"; // NOI18N
0866: }
0867: }
0868:
0869: /**
0870: * Get the visual column corresponding to the position after pressing the
0871: * TAB key.
0872: *
0873: * @param doc
0874: * document to work with
0875: * @param offset
0876: * position at which the TAB was pressed
0877: */
0878: public static int getNextTabColumn(BaseDocument doc, int offset)
0879: throws BadLocationException {
0880: int col = getVisualColumn(doc, offset);
0881: int tabSize = doc.getFormatter().getSpacesPerTab();
0882: return (col + tabSize) / tabSize * tabSize;
0883: }
0884:
0885: public static void setStatusText(JTextComponent c, String text) {
0886: StatusBar sb = getEditorUI(c).getStatusBar();
0887: if (sb != null) {
0888: sb.setText(StatusBar.CELL_MAIN, text);
0889: }
0890: }
0891:
0892: public static void setStatusText(JTextComponent c, String text,
0893: Coloring extraColoring) {
0894: StatusBar sb = getEditorUI(c).getStatusBar();
0895: if (sb != null) {
0896: sb.setText(StatusBar.CELL_MAIN, text, extraColoring);
0897: }
0898: }
0899:
0900: public static void setStatusBoldText(JTextComponent c, String text) {
0901: StatusBar sb = getEditorUI(c).getStatusBar();
0902: if (sb != null) {
0903: sb.setBoldText(StatusBar.CELL_MAIN, text);
0904: }
0905: }
0906:
0907: public static String getStatusText(JTextComponent c) {
0908: StatusBar sb = getEditorUI(c).getStatusBar();
0909: return (sb != null) ? sb.getText(StatusBar.CELL_MAIN) : null;
0910: }
0911:
0912: public static void clearStatusText(JTextComponent c) {
0913: setStatusText(c, ""); // NOI18N
0914: }
0915:
0916: public static void insertMark(BaseDocument doc, Mark mark,
0917: int offset) throws BadLocationException,
0918: InvalidMarkException {
0919: doc.op.insertMark(mark, offset);
0920: }
0921:
0922: public static void moveMark(BaseDocument doc, Mark mark,
0923: int newOffset) throws BadLocationException,
0924: InvalidMarkException {
0925: doc.op.moveMark(mark, newOffset);
0926: }
0927:
0928: public static void returnFocus() {
0929: JTextComponent c = getLastActiveComponent();
0930: if (c != null) {
0931: requestFocus(c);
0932: }
0933: }
0934:
0935: public static void requestFocus(JTextComponent c) {
0936: if (c != null) {
0937: boolean ok = false;
0938: BaseKit kit = getKit(c);
0939: if (kit != null) {
0940: Class fcc = kit.getFocusableComponentClass(c);
0941: if (fcc != null) {
0942: Container container = SwingUtilities
0943: .getAncestorOfClass(fcc, c);
0944: if (container != null) {
0945: container.requestFocus();
0946: ok = true;
0947: }
0948: }
0949: }
0950:
0951: if (!ok) {
0952: Frame f = EditorUI.getParentFrame(c);
0953: if (f != null) {
0954: f.requestFocus();
0955: }
0956: c.requestFocus();
0957: }
0958: }
0959: }
0960:
0961: public static void runInEventDispatchThread(Runnable r) {
0962: if (SwingUtilities.isEventDispatchThread()) {
0963: r.run();
0964: } else {
0965: SwingUtilities.invokeLater(r);
0966: }
0967: }
0968:
0969: public static String debugPosition(BaseDocument doc, int offset) {
0970: String ret;
0971:
0972: if (offset >= 0) {
0973: try {
0974: int line = getLineOffset(doc, offset) + 1;
0975: int col = getVisualColumn(doc, offset) + 1;
0976: ret = String.valueOf(line) + ":" + String.valueOf(col); // NOI18N
0977: } catch (BadLocationException e) {
0978: ret = LocaleSupport.getString(WRONG_POSITION_LOCALE)
0979: + ' ' + offset + " > " + doc.getLength(); // NOI18N
0980: }
0981: } else {
0982: ret = String.valueOf(offset);
0983: }
0984:
0985: return ret;
0986: }
0987:
0988: /**
0989: * Display the identity of the document together with the title property and
0990: * stream-description property.
0991: */
0992: public static String debugDocument(Document doc) {
0993: return "<" + System.identityHashCode(doc) + ", title='"
0994: + doc.getProperty(Document.TitleProperty)
0995: + "', stream='"
0996: + doc.getProperty(Document.StreamDescriptionProperty)
0997: + ", " + doc.toString() + ">";
0998: }
0999:
1000: public static void performAction(Action a, ActionEvent evt,
1001: JTextComponent target) {
1002: if (a instanceof BaseAction) {
1003: ((BaseAction) a).actionPerformed(evt, target);
1004: } else {
1005: a.actionPerformed(evt);
1006: }
1007: }
1008:
1009: public static JTextComponent getLastActiveComponent() {
1010: return Registry.getMostActiveComponent();
1011: }
1012:
1013: /**
1014: * Helper method to obtain instance of EditorUI (extended UI) from the
1015: * existing JTextComponent. It doesn't require any document locking.
1016: *
1017: * @param target
1018: * JTextComponent for which the extended UI should be obtained
1019: * @return extended ui instance or null if the component.getUI() does not
1020: * return BaseTextUI instance.
1021: */
1022: public static EditorUI getEditorUI(JTextComponent target) {
1023: TextUI ui = target.getUI();
1024: return (ui instanceof BaseTextUI) ? ((BaseTextUI) ui)
1025: .getEditorUI() : null;
1026: }
1027:
1028: /**
1029: * Helper method to obtain instance of editor kit from existing
1030: * JTextComponent. If the kit of the component is not an instance of the
1031: * <tt>org.netbeans.editor.BaseKit</tt> the method returns null. The
1032: * method doesn't require any document locking.
1033: *
1034: * @param target
1035: * JTextComponent for which the editor kit should be obtained
1036: * @return BaseKit instance or null
1037: */
1038: public static BaseKit getKit(JTextComponent target) {
1039: EditorKit ekit = target.getUI().getEditorKit(target);
1040: return (ekit instanceof BaseKit) ? (BaseKit) ekit : null;
1041: }
1042:
1043: /**
1044: * Helper method to obtain editor kit class from existing JTextComponent.
1045: * This method is useful for example when dealing with Settings. The method
1046: * doesn't require any document locking.
1047: *
1048: * @param target
1049: * JTextComponent for which the editor kit should be obtained
1050: * @return editor kit class
1051: */
1052: public static Class getKitClass(JTextComponent target) {
1053: return (target != null) ? target.getUI().getEditorKit(target)
1054: .getClass() : null;
1055: }
1056:
1057: /**
1058: * Helper method to obtain instance of BaseDocument from JTextComponent. If
1059: * the document of the component is not an instance of the
1060: * <tt>org.netbeans.editor.BaseDocument</tt> the method returns null. The
1061: * method doesn't require any document locking.
1062: *
1063: * @param target
1064: * JTextComponent for which the document should be obtained
1065: * @return BaseDocument instance or null
1066: */
1067: public static BaseDocument getDocument(JTextComponent target) {
1068: Document doc = target.getDocument();
1069: return (doc instanceof BaseDocument) ? (BaseDocument) doc
1070: : null;
1071: }
1072:
1073: /**
1074: * Get the syntax-support class that belongs to the document of the given
1075: * component. Besides using directly this method, the <tt>SyntaxSupport</tt>
1076: * can be obtained by calling <tt>doc.getSyntaxSupport()</tt>. The method
1077: * can return null in case the document is not an instance of the
1078: * BaseDocument. The method doesn't require any document locking.
1079: *
1080: * @param target
1081: * JTextComponent for which the syntax-support should be obtained
1082: * @return SyntaxSupport instance or null
1083: */
1084: public static SyntaxSupport getSyntaxSupport(JTextComponent target) {
1085: Document doc = target.getDocument();
1086: return (doc instanceof BaseDocument) ? ((BaseDocument) doc)
1087: .getSyntaxSupport() : null;
1088: }
1089:
1090: /**
1091: * Creates nice textual description of sequence of KeyStrokes. Usable for
1092: * displaying MultiKeyBindings. The keyStrokes are delimited by space.
1093: *
1094: * @param Array
1095: * of KeyStrokes representing the actual sequence.
1096: * @return String describing the KeyStroke sequence.
1097: */
1098: public static String keySequenceToString(KeyStroke[] seq) {
1099: StringBuffer sb = new StringBuffer();
1100: for (int i = 0; i < seq.length; i++) {
1101: if (i > 0)
1102: sb.append(' '); // NOI18N
1103: sb.append(keyStrokeToString(seq[i]));
1104: }
1105: return sb.toString();
1106: }
1107:
1108: /**
1109: * Creates nice textual representation of KeyStroke. Modifiers and an actual
1110: * key label are concated by plus signs
1111: *
1112: * @param the
1113: * KeyStroke to get description of
1114: * @return String describing the KeyStroke
1115: */
1116: public static String keyStrokeToString(KeyStroke stroke) {
1117: String modifText = KeyEvent.getKeyModifiersText(stroke
1118: .getModifiers());
1119: String keyText = (stroke.getKeyCode() == KeyEvent.VK_UNDEFINED) ? String
1120: .valueOf(stroke.getKeyChar())
1121: : getKeyText(stroke.getKeyCode());
1122: if (modifText.length() > 0)
1123: return modifText + '+' + keyText;
1124: else
1125: return keyText;
1126: }
1127:
1128: /**
1129: * @return slight modification of what KeyEvent.getKeyText() returns. The
1130: * numpad Left, Right, Down, Up get extra result.
1131: */
1132: private static String getKeyText(int keyCode) {
1133: String ret = KeyEvent.getKeyText(keyCode);
1134: if (ret != null) {
1135: switch (keyCode) {
1136: case KeyEvent.VK_KP_DOWN:
1137: ret = prefixNumpad(ret, KeyEvent.VK_DOWN);
1138: break;
1139: case KeyEvent.VK_KP_LEFT:
1140: ret = prefixNumpad(ret, KeyEvent.VK_LEFT);
1141: break;
1142: case KeyEvent.VK_KP_RIGHT:
1143: ret = prefixNumpad(ret, KeyEvent.VK_RIGHT);
1144: break;
1145: case KeyEvent.VK_KP_UP:
1146: ret = prefixNumpad(ret, KeyEvent.VK_UP);
1147: break;
1148: }
1149: }
1150: return ret;
1151: }
1152:
1153: private static String prefixNumpad(String key, int testKeyCode) {
1154: if (key.equals(KeyEvent.getKeyText(testKeyCode))) {
1155: key = LocaleSupport.getString("key-prefix-numpad") + key;
1156: }
1157: return key;
1158: }
1159:
1160: }
|