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.editor.hints.borrowed;
043:
044: import java.awt.Color;
045: import java.awt.Dimension;
046: import java.awt.event.KeyEvent;
047:
048: import javax.swing.*;
049: import javax.swing.BorderFactory;
050: import javax.swing.event.ListSelectionListener;
051: import javax.swing.plaf.TextUI;
052: import javax.swing.text.JTextComponent;
053: import javax.swing.text.Keymap;
054: import javax.swing.text.EditorKit;
055:
056: import org.netbeans.editor.*;
057: import org.netbeans.editor.ext.ExtSettingsDefaults;
058: import org.netbeans.editor.ext.ExtSettingsNames;
059: import org.netbeans.spi.editor.hints.Fix;
060: import org.netbeans.spi.editor.hints.LazyFixList;
061:
062: /**
063: * Pane displaying the completion view and accompanying components
064: * like label for title etc.
065: *
066: * @author Miloslav Metelka, Martin Roskanin, Dusan Balek
067: * @version 1.00
068: */
069:
070: public class ScrollCompletionPane extends JScrollPane implements
071: SettingsChangeListener {
072:
073: private static final String COMPLETION_UP = "completion-up"; //NOI18N
074: private static final String COMPLETION_DOWN = "completion-down"; //NOI18N
075: private static final String COMPLETION_PGUP = "completion-pgup"; //NOI18N
076: private static final String COMPLETION_PGDN = "completion-pgdn"; //NOI18N
077: private static final String COMPLETION_BEGIN = "completion-begin"; //NOI18N
078: private static final String COMPLETION_END = "completion-end"; //NOI18N
079:
080: private static final int ACTION_COMPLETION_UP = 1;
081: private static final int ACTION_COMPLETION_DOWN = 2;
082: private static final int ACTION_COMPLETION_PGUP = 3;
083: private static final int ACTION_COMPLETION_PGDN = 4;
084: private static final int ACTION_COMPLETION_BEGIN = 5;
085: private static final int ACTION_COMPLETION_END = 6;
086:
087: private JTextComponent component;
088:
089: private ListCompletionView view;
090: private JLabel topLabel;
091:
092: private Dimension minSize;
093: private Dimension maxSize;
094: private Dimension scrollBarSize;
095:
096: public ScrollCompletionPane(JTextComponent component,
097: LazyFixList fixes, String title,
098: ListSelectionListener listener) {
099: this (component, fixes, title, listener, null);
100: }
101:
102: public ScrollCompletionPane(JTextComponent component,
103: LazyFixList fixes, String title,
104: ListSelectionListener listener, Dimension maxSize) {
105: this .component = component;
106:
107: // Compute size of the scrollbars
108: Dimension smallSize = super .getPreferredSize();
109: setHorizontalScrollBarPolicy(HORIZONTAL_SCROLLBAR_NEVER);
110: setVerticalScrollBarPolicy(VERTICAL_SCROLLBAR_ALWAYS);
111: scrollBarSize = super .getPreferredSize();
112: scrollBarSize.width -= smallSize.width;
113: scrollBarSize.height -= smallSize.height;
114: setVerticalScrollBarPolicy(VERTICAL_SCROLLBAR_AS_NEEDED);
115: setBorder(BorderFactory
116: .createCompoundBorder(
117: BorderFactory
118: .createLineBorder(java.awt.SystemColor.controlDkShadow),
119: BorderFactory.createEmptyBorder(4, 4, 4, 4)));
120: setViewportBorder(null);
121: Settings.addSettingsChangeListener(this );
122: settingsChange(null); // initialize sizes
123: if (maxSize != null) {
124: this .maxSize = maxSize;
125: setMaximumSize(maxSize);
126: }
127:
128: // Add the completion view
129: view = new ListCompletionView();
130: setBackground(view.getBackground());
131: // view.addListSelectionListener(listener);
132: view.setResult(fixes);
133: resetViewSize();
134: setViewportView(view);
135:
136: setTitle(title);
137: installKeybindings();
138: setFocusable(false);
139: view.setFocusable(false);
140: }
141:
142: public ListCompletionView getView() {
143: return view;
144: }
145:
146: public void reset(LazyFixList fixes, String title) {
147: view.setResult(fixes);
148: resetViewSize();
149: setTitle(title);
150: }
151:
152: public Fix getSelectedFix() {
153: Object ret = view.getSelectedValue();
154: return ret instanceof Fix ? (Fix) ret : null;
155: }
156:
157: public void settingsChange(SettingsChangeEvent evt) {
158: Class kitClass = Utilities.getKitClass(component);
159: if (kitClass != null) {
160: minSize = (Dimension) SettingsUtil.getValue(kitClass,
161: ExtSettingsNames.COMPLETION_PANE_MIN_SIZE,
162: ExtSettingsDefaults.defaultCompletionPaneMinSize);
163: setMinimumSize(minSize);
164:
165: maxSize = (Dimension) SettingsUtil.getValue(kitClass,
166: ExtSettingsNames.COMPLETION_PANE_MAX_SIZE,
167: ExtSettingsDefaults.defaultCompletionPaneMaxSize);
168: setMaximumSize(maxSize);
169: }
170: }
171:
172: public Dimension getPreferredSize() {
173: Dimension ps = super .getPreferredSize();
174:
175: /* Add size of the vertical scrollbar by default. This could be improved
176: * to be done only if the height exceeds the bounds. */
177: int width = ps.width + scrollBarSize.width;
178: boolean displayHorizontalScrollbar = width > maxSize.width;
179: width = Math.max(Math.max(width, minSize.width),
180: getTitleComponentPreferredSize().width);
181: width = Math.min(width, maxSize.width);
182:
183: int height = displayHorizontalScrollbar ? ps.height
184: + scrollBarSize.height : ps.height;
185: height = Math.min(height, maxSize.height);
186: height = Math.max(height, minSize.height);
187: return new Dimension(width, height);
188: }
189:
190: private void resetViewSize() {
191: Dimension viewSize = view.getPreferredSize();
192: if (viewSize.width > maxSize.width - scrollBarSize.width) {
193: viewSize.width = maxSize.width - scrollBarSize.width;
194: view.setPreferredSize(viewSize);
195: }
196: }
197:
198: private void setTitle(String title) {
199: if (title == null) {
200: if (topLabel != null) {
201: setColumnHeader(null);
202: topLabel = null;
203: }
204: } else {
205: if (topLabel != null) {
206: topLabel.setText(title);
207: } else {
208: topLabel = new JLabel(title);
209: topLabel.setForeground(Color.blue);
210: topLabel.setBorder(BorderFactory.createEmptyBorder(0,
211: 2, 0, 2));
212: setColumnHeaderView(topLabel);
213: }
214: }
215: }
216:
217: private Dimension getTitleComponentPreferredSize() {
218: return topLabel != null ? topLabel.getPreferredSize()
219: : new Dimension();
220: }
221:
222: /** Attempt to find the editor keystroke for the given editor action. */
223: private KeyStroke[] findEditorKeys(String editorActionName,
224: KeyStroke defaultKey) {
225: // This method is implemented due to the issue
226: // #25715 - Attempt to search keymap for the keybinding that logically corresponds to the action
227: KeyStroke[] ret = new KeyStroke[] { defaultKey };
228: if (component != null) {
229: TextUI ui = component.getUI();
230: Keymap km = component.getKeymap();
231: if (ui != null && km != null) {
232: EditorKit kit = ui.getEditorKit(component);
233: if (kit instanceof BaseKit) {
234: Action a = ((BaseKit) kit)
235: .getActionByName(editorActionName);
236: if (a != null) {
237: KeyStroke[] keys = km.getKeyStrokesForAction(a);
238: if (keys != null && keys.length > 0) {
239: ret = keys;
240: }
241: }
242: }
243: }
244: }
245: return ret;
246: }
247:
248: private void registerKeybinding(int action, String actionName,
249: KeyStroke stroke, String editorActionName) {
250: KeyStroke[] keys = findEditorKeys(editorActionName, stroke);
251: for (int i = 0; i < keys.length; i++) {
252: getInputMap().put(keys[i], actionName);
253: }
254: getActionMap()
255: .put(actionName, new CompletionPaneAction(action));
256: }
257:
258: private void installKeybindings() {
259: // Register up key
260: registerKeybinding(ACTION_COMPLETION_UP, COMPLETION_UP,
261: KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0),
262: BaseKit.upAction);
263:
264: // Register down key
265: registerKeybinding(ACTION_COMPLETION_DOWN, COMPLETION_DOWN,
266: KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0),
267: BaseKit.downAction);
268:
269: // Register PgDn key
270: registerKeybinding(ACTION_COMPLETION_PGDN, COMPLETION_PGDN,
271: KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_DOWN, 0),
272: BaseKit.pageDownAction);
273:
274: // Register PgUp key
275: registerKeybinding(ACTION_COMPLETION_PGUP, COMPLETION_PGUP,
276: KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_UP, 0),
277: BaseKit.pageUpAction);
278:
279: // Register home key
280: registerKeybinding(ACTION_COMPLETION_BEGIN, COMPLETION_BEGIN,
281: KeyStroke.getKeyStroke(KeyEvent.VK_HOME, 0),
282: BaseKit.beginLineAction);
283:
284: // Register end key
285: registerKeybinding(ACTION_COMPLETION_END, COMPLETION_END,
286: KeyStroke.getKeyStroke(KeyEvent.VK_END, 0),
287: BaseKit.endLineAction);
288: }
289:
290: private class CompletionPaneAction extends AbstractAction {
291: private int action;
292:
293: private CompletionPaneAction(int action) {
294: this .action = action;
295: }
296:
297: public void actionPerformed(
298: java.awt.event.ActionEvent actionEvent) {
299: switch (action) {
300: case ACTION_COMPLETION_UP:
301: view.up();
302: break;
303: case ACTION_COMPLETION_DOWN:
304: view.down();
305: break;
306: case ACTION_COMPLETION_PGUP:
307: view.pageUp();
308: break;
309: case ACTION_COMPLETION_PGDN:
310: view.pageDown();
311: break;
312: case ACTION_COMPLETION_BEGIN:
313: view.begin();
314: break;
315: case ACTION_COMPLETION_END:
316: view.end();
317: break;
318: }
319: }
320: }
321: }
|