001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.netbeans.modules.xml.text.completion;
043:
044: import java.awt.Color;
045: import java.awt.Component;
046: import java.awt.Font;
047: import java.awt.Graphics;
048: import java.awt.event.KeyEvent;
049: import java.io.IOException;
050:
051: import javax.swing.text.*;
052: import javax.swing.Icon;
053:
054: import org.netbeans.editor.*; //import org.netbeans.editor.ext.*;
055: import org.netbeans.editor.Utilities;
056: import javax.swing.JLabel;
057: import org.netbeans.api.editor.completion.Completion;
058: import org.netbeans.spi.editor.completion.CompletionItem;
059:
060: import org.netbeans.editor.ext.CompletionQuery.ResultItem;
061: import org.netbeans.editor.ext.ExtFormatter;
062: import org.netbeans.spi.editor.completion.CompletionTask;
063: import org.netbeans.spi.editor.completion.support.AsyncCompletionTask;
064:
065: /**
066: * This class carries result information required by NetBeans Editor module.
067: *
068: * @author Petr Kuzel
069: * @author Sandeep Randhawa
070: */
071: class XMLResultItem implements ResultItem, CompletionItem {
072:
073: private static final int XML_ITEMS_SORT_PRIORITY = 20;
074: // text to be diplayed to user
075: public String displayText;
076: private String replacementText;
077: // icon to be diplayed
078: public javax.swing.Icon icon;
079: public Color foreground = Color.black;
080: public Color background = Color.white;
081: public Color selectionForeground = Color.black;
082: public Color selectionBackground = new Color(204, 204, 255);
083: private static JLabel rubberStamp = new JLabel();
084: private XMLCompletionResultItemPaintComponent component;
085: private boolean shift = false;
086:
087: static {
088: rubberStamp.setOpaque(true);
089: }
090:
091: /**
092: *
093: * @param replacementText replacement text that is used as display name too
094: */
095: public XMLResultItem(String replacementText) {
096: this (replacementText, null);
097: }
098:
099: /**
100: * @param displayText text to display or null if replacementText is OK
101: */
102: public XMLResultItem(String replacementText, String displayText) {
103: this .replacementText = replacementText;
104: this .displayText = displayText != null ? displayText
105: : replacementText;
106: }
107:
108: /** Creates new XMLResultItem
109: * @param displayText The string value that will be displayed in the completion window and will hence
110: * be the replacement text if selected.
111: * @param icon The icon that will be displayed for this element.
112: * @param foreground The foreground color of the text
113: * @param background The background color of the text
114: * @param selectionForeground The foreground color of the selected text
115: * @param selectionBackground The background color of the selected text
116: */
117: public XMLResultItem(String displayText, javax.swing.Icon icon,
118: Color foreground, Color background,
119: Color selectionForeground, Color selectionBackground) {
120: this .displayText = displayText;
121: this .icon = icon;
122: this .foreground = foreground;
123: this .background = background;
124: this .selectionForeground = selectionForeground;
125: this .selectionBackground = selectionBackground;
126: }
127:
128: /**
129: * Insert following text into document.
130: */
131: public String getReplacementText(int modifiers) {
132: return displayText;
133: }
134:
135: protected Icon getIcon() {
136: return icon;
137: }
138:
139: /**
140: * Actually replaces a piece of document by passes text.
141: * @param component a document source
142: * @param text a string to be inserted
143: * @param offset the target offset
144: * @param len a length that should be removed before inserting text
145: */
146: boolean replaceText(JTextComponent component, String text,
147: int offset, int len) {
148: BaseDocument doc = (BaseDocument) component.getDocument();
149: doc.atomicLock();
150: try {
151: String currentText = doc.getText(offset,
152: (doc.getLength() - offset) < text.length() ? (doc
153: .getLength() - offset) : text.length());
154: if (!text.equals(currentText)) {
155: doc.remove(offset, len);
156: doc.insertString(offset, text, null);
157: } else {
158: int newCaretPos = component.getCaret().getDot()
159: + text.length() - len;
160: //#82242 workaround - the problem is that in some situations
161: //1) result item is created and it remembers the remove length
162: //2) document is changed
163: //3) RI is substituted.
164: //this situation shouldn't happen imho and is a problem of CC infrastructure
165: component.setCaretPosition(newCaretPos < doc
166: .getLength() ? newCaretPos : doc.getLength());
167: }
168: //reformat the line
169: //((ExtFormatter)doc.getFormatter()).reformat(doc, Utilities.getRowStart(doc, offset), offset+text.length(), true);
170: } catch (BadLocationException exc) {
171: return false; //not sucessfull
172: // } catch (IOException e) {
173: // return false;
174: } finally {
175: doc.atomicUnlock();
176: }
177: return true;
178: }
179:
180: public boolean substituteCommonText(JTextComponent c, int offset,
181: int len, int subLen) {
182: return replaceText(c, getReplacementText(0)
183: .substring(0, subLen), offset, len);
184: }
185:
186: /**
187: * Just translate <code>shift</code> to proper modifier
188: */
189: public final boolean substituteText(JTextComponent c, int offset,
190: int len, boolean shift) {
191: int modifier = shift ? java.awt.event.InputEvent.SHIFT_MASK : 0;
192: return substituteText(c, offset, len, modifier);
193: }
194:
195: public boolean substituteText(JTextComponent c, int offset,
196: int len, int modifiers) {
197: return replaceText(c, getReplacementText(modifiers), offset,
198: len);
199: }
200:
201: /** @return Properly colored JLabel with text gotten from <CODE>getPaintText()</CODE>. */
202: public java.awt.Component getPaintComponent(javax.swing.JList list,
203: boolean isSelected, boolean cellHasFocus) {
204: // The space is prepended to avoid interpretation as HTML Label
205: if (getIcon() != null)
206: rubberStamp.setIcon(getIcon());
207:
208: rubberStamp.setText(displayText);
209: if (isSelected) {
210: rubberStamp.setBackground(selectionBackground);
211: rubberStamp.setForeground(selectionForeground);
212: } else {
213: rubberStamp.setBackground(background);
214: rubberStamp.setForeground(foreground);
215: }
216: return rubberStamp;
217: }
218:
219: public final String getItemText() {
220: return replacementText;
221: }
222:
223: public String toString() {
224: return getItemText();
225: }
226:
227: Color getPaintColor() {
228: return Color.BLUE;
229: }
230:
231: ////////////////////////////////////////////////////////////////////////////////
232: ///////////////////methods from CompletionItem interface////////////////////////
233: ////////////////////////////////////////////////////////////////////////////////
234: public CompletionTask createDocumentationTask() {
235: return null; //no documentation supported for now
236: //return new AsyncCompletionTask(new DocQuery(this));
237: }
238:
239: public CompletionTask createToolTipTask() {
240: return null;
241: }
242:
243: public void defaultAction(JTextComponent component) {
244: int substOffset = getSubstituteOffset();
245: if (substOffset == -1)
246: substOffset = component.getCaretPosition();
247:
248: if (!shift)
249: Completion.get().hideAll();
250: substituteText(component, substOffset, component
251: .getCaretPosition()
252: - substOffset, shift);
253: }
254:
255: static int substituteOffset = -1;
256:
257: public int getSubstituteOffset() {
258: return substituteOffset;
259: }
260:
261: public CharSequence getInsertPrefix() {
262: return getItemText();
263: }
264:
265: public Component getPaintComponent(boolean isSelected) {
266: XMLCompletionResultItemPaintComponent component = new XMLCompletionResultItemPaintComponent.StringPaintComponent(
267: getPaintColor());
268: component.setSelected(isSelected);
269: component.setString(getItemText());
270: return component;
271: }
272:
273: public int getPreferredWidth(Graphics g, Font defaultFont) {
274: Component renderComponent = getPaintComponent(false);
275: return renderComponent.getPreferredSize().width;
276: }
277:
278: public int getSortPriority() {
279: return XML_ITEMS_SORT_PRIORITY;
280: }
281:
282: public CharSequence getSortText() {
283: return getItemText();
284: }
285:
286: public boolean instantSubstitution(JTextComponent component) {
287: defaultAction(component);
288: return true;
289: }
290:
291: public void processKeyEvent(KeyEvent e) {
292: shift = (e.getKeyCode() == KeyEvent.VK_ENTER
293: && e.getID() == KeyEvent.KEY_PRESSED && e.isShiftDown());
294: }
295:
296: public void render(Graphics g, Font defaultFont,
297: Color defaultColor, Color backgroundColor, int width,
298: int height, boolean selected) {
299: Component renderComponent = getPaintComponent(selected);
300: renderComponent.setFont(defaultFont);
301: renderComponent.setForeground(defaultColor);
302: renderComponent.setBackground(backgroundColor);
303: renderComponent.setBounds(0, 0, width, height);
304: ((XMLCompletionResultItemPaintComponent) renderComponent)
305: .paintComponent(g);
306: }
307:
308: }
|