1 19 20 package org.netbeans.modules.editor.completion; 21 22 import java.awt.BorderLayout ; 23 import java.awt.Color ; 24 import java.awt.Dimension ; 25 import java.awt.Rectangle ; 26 import java.awt.event.ActionEvent ; 27 import java.awt.event.KeyEvent ; 28 import java.awt.event.MouseAdapter ; 29 import java.awt.event.MouseEvent ; 30 import java.lang.ref.Reference ; 31 import java.lang.ref.WeakReference ; 32 import java.util.List ; 33 import java.util.Stack ; 34 import javax.swing.Action ; 35 import javax.swing.BorderFactory ; 36 import javax.swing.JComponent ; 37 import javax.swing.JLabel ; 38 import javax.swing.JPanel ; 39 import javax.swing.JToolTip ; 40 import javax.swing.KeyStroke ; 41 import javax.swing.SwingConstants ; 42 import javax.swing.SwingUtilities ; 43 import javax.swing.event.ListSelectionListener ; 44 import javax.swing.text.JTextComponent ; 45 import org.netbeans.spi.editor.completion.CompletionDocumentation; 46 import org.netbeans.spi.editor.completion.CompletionItem; 47 import org.openide.util.NbBundle; 48 49 54 public final class CompletionLayout { 55 56 public static final int COMPLETION_ITEM_HEIGHT = 16; 57 58 63 private static final int COMPLETION_ANCHOR_HORIZONTAL_SHIFT = 22; 64 65 68 static final int POPUP_VERTICAL_GAP = 1; 69 70 private Reference <JTextComponent > editorComponentRef; 71 72 private final CompletionPopup completionPopup; 73 private final DocPopup docPopup; 74 private final TipPopup tipPopup; 75 76 private Stack <CompletionLayoutPopup> visiblePopups; 77 78 CompletionLayout() { 79 completionPopup = new CompletionPopup(); 80 completionPopup.setLayout(this); 81 completionPopup.setPreferDisplayAboveCaret(true); 82 docPopup = new DocPopup(); 83 docPopup.setLayout(this); 84 docPopup.setPreferDisplayAboveCaret(false); 85 tipPopup = new TipPopup(); 86 tipPopup.setLayout(this); 87 tipPopup.setPreferDisplayAboveCaret(true); 88 visiblePopups = new Stack <CompletionLayoutPopup>(); 89 } 90 91 public JTextComponent getEditorComponent() { 92 return (editorComponentRef != null) 93 ? editorComponentRef.get() 94 : null; 95 } 96 97 public void setEditorComponent(JTextComponent editorComponent) { 98 hideAll(); 99 this.editorComponentRef = new WeakReference <JTextComponent >(editorComponent); 100 } 101 102 private void hideAll() { 103 completionPopup.hide(); 104 docPopup.hide(); 105 tipPopup.hide(); 106 visiblePopups.clear(); 107 } 108 109 public void showCompletion(List data, String title, int anchorOffset, 110 ListSelectionListener listSelectionListener, boolean showShortcutHints, int selectedIndex) { 111 completionPopup.show(data, title, anchorOffset, listSelectionListener, showShortcutHints, selectedIndex); 112 if (!visiblePopups.contains(completionPopup)) 113 visiblePopups.push(completionPopup); 114 } 115 116 public boolean hideCompletion() { 117 if (completionPopup.isVisible()) { 118 completionPopup.hide(); 119 completionPopup.completionScrollPane = null; 120 visiblePopups.remove(completionPopup); 121 return true; 122 } else { return false; 124 } 125 } 126 127 public boolean isCompletionVisible() { 128 return completionPopup.isVisible(); 129 } 130 131 public CompletionItem getSelectedCompletionItem() { 132 return completionPopup.getSelectedCompletionItem(); 133 } 134 135 public void processKeyEvent(KeyEvent evt) { 136 for (int i = visiblePopups.size() - 1; i >= 0; i--) { 137 CompletionLayoutPopup popup = visiblePopups.get(i); 138 popup.processKeyEvent(evt); 139 if (evt.isConsumed()) 140 return; 141 } 142 } 143 144 public void showDocumentation(CompletionDocumentation doc, int anchorOffset) { 145 docPopup.show(doc, anchorOffset); 146 if (!visiblePopups.contains(docPopup)) 147 visiblePopups.push(docPopup); 148 } 149 150 public boolean hideDocumentation() { 151 if (docPopup.isVisible()) { 152 docPopup.getDocumentationScrollPane().currentDocumentation = null; 153 docPopup.clearHistory(); 154 docPopup.hide(); 155 visiblePopups.remove(docPopup); 156 return true; 157 } else { return false; 159 } 160 } 161 162 public boolean isDocumentationVisible() { 163 return docPopup.isVisible(); 164 } 165 166 public void clearDocumentationHistory() { 167 docPopup.clearHistory(); 168 } 169 170 public void showToolTip(JToolTip toolTip, int anchorOffset) { 171 tipPopup.show(toolTip, anchorOffset); 172 if (!visiblePopups.contains(tipPopup)) 173 visiblePopups.push(tipPopup); 174 } 175 176 public boolean hideToolTip() { 177 if (tipPopup.isVisible()) { 178 tipPopup.hide(); 179 visiblePopups.remove(tipPopup); 180 return true; 181 } else { return false; 183 } 184 } 185 186 public boolean isToolTipVisible() { 187 return tipPopup.isVisible(); 188 } 189 190 209 void updateLayout(CompletionLayoutPopup popup) { 210 popup.resetPreferredSize(); 212 213 if (popup == completionPopup) { if (isToolTipVisible()) { 215 boolean wantAboveCaret = !tipPopup.isDisplayAboveCaret(); 217 if (completionPopup.isEnoughSpace(wantAboveCaret)) { 218 completionPopup.showAlongAnchorBounds(wantAboveCaret); 219 } else { Rectangle occupiedBounds = popup.getAnchorOffsetBounds(); 221 occupiedBounds = tipPopup.unionBounds(occupiedBounds); 222 completionPopup.showAlongOccupiedBounds(occupiedBounds, 223 tipPopup.isDisplayAboveCaret()); 224 } 225 226 } else { popup.showAlongAnchorBounds(); 228 } 229 230 if (docPopup.isVisible() 232 && (docPopup.isOverlapped(popup) || docPopup.isOverlapped(tipPopup) 233 || docPopup.getAnchorOffset() != completionPopup.getAnchorOffset() 234 || !docPopup.isShowRetainedPreferredSize()) 235 ) { 236 updateLayout(docPopup); 237 } 238 239 } else if (popup == docPopup) { if (isCompletionVisible()) { 241 popup.setAnchorOffset(completionPopup.getAnchorOffset()); 243 } 244 245 Rectangle occupiedBounds = popup.getAnchorOffsetBounds(); 246 occupiedBounds = tipPopup.unionBounds(completionPopup.unionBounds(occupiedBounds)); 247 docPopup.showAlongOccupiedBounds(occupiedBounds); 248 249 } else if (popup == tipPopup) { popup.showAlongAnchorBounds(); if (completionPopup.isOverlapped(popup) || docPopup.isOverlapped(popup)) { 252 updateLayout(completionPopup); 254 } 255 } 256 } 257 258 CompletionPopup testGetCompletionPopup() { 259 return completionPopup; 260 } 261 262 private static final class CompletionPopup extends CompletionLayoutPopup { 263 264 private CompletionScrollPane completionScrollPane; 265 266 public void show(List data, String title, int anchorOffset, 267 ListSelectionListener listSelectionListener, boolean showShortcutHints, int selectedIndex) { 268 269 JTextComponent editorComponent = getEditorComponent(); 270 if (editorComponent == null) { 271 return; 272 } 273 274 Dimension lastSize; 275 int lastAnchorOffset = getAnchorOffset(); 276 277 if (isVisible() && ((getContentComponent() == completionScrollPane)^(showShortcutHints))) { 278 lastSize = getContentComponent().getSize(); 279 resetPreferredSize(); 280 281 } else { lastSize = new Dimension (0, 0); 284 completionScrollPane = new CompletionScrollPane( 285 editorComponent, listSelectionListener, 286 new MouseAdapter () { 287 public void mouseClicked(MouseEvent evt) { 288 JTextComponent c = getEditorComponent(); 289 if (SwingUtilities.isLeftMouseButton(evt)) { 290 if (c != null && evt.getClickCount() == 2 ) { 291 CompletionItem selectedItem 292 = completionScrollPane.getSelectedCompletionItem(); 293 if (selectedItem != null) { 294 selectedItem.defaultAction(c); 295 } 296 } 297 } 298 } 299 } 300 ); 301 302 completionScrollPane.getViewport().getView().setFont(getEditorComponent().getFont()); 303 304 if (showShortcutHints) { 305 JPanel panel = new JPanel (); 306 panel.setLayout(new BorderLayout ()); 307 panel.add(completionScrollPane, BorderLayout.CENTER); 308 JLabel label = new JLabel (); 309 label.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createMatteBorder(0, 0, 0, 1, Color.white), 310 BorderFactory.createCompoundBorder(BorderFactory.createMatteBorder(0, 1, 1, 1, Color.gray), BorderFactory.createEmptyBorder(2, 2, 2, 2)))); 311 label.setFont(label.getFont().deriveFont((float)label.getFont().getSize() - 2)); 312 label.setHorizontalAlignment(SwingConstants.RIGHT); 313 label.setText(NbBundle.getMessage(CompletionLayout.class, "TXT_completion_shrtcut_tips")); panel.add(label, BorderLayout.SOUTH); 315 setContentComponent(panel); 316 } else { 317 setContentComponent(completionScrollPane); 318 } 319 } 320 completionScrollPane.setData(data, title, selectedIndex); 322 setAnchorOffset(anchorOffset); 323 324 Dimension prefSize = getPreferredSize(); 325 326 boolean changePopupSize; 327 if (isVisible()) { 328 changePopupSize = (prefSize.height != lastSize.height) 329 || (prefSize.width != lastSize.width) 330 || anchorOffset != lastAnchorOffset; 331 332 } else { changePopupSize = true; 334 } 335 336 if (changePopupSize) { 337 getLayout().updateLayout(this); 340 341 } } 343 344 public CompletionItem getSelectedCompletionItem() { 345 return isVisible() ? completionScrollPane.getSelectedCompletionItem() : null; 346 } 347 348 public void processKeyEvent(KeyEvent evt) { 349 if (isVisible()) { 350 Object actionMapKey = completionScrollPane.getInputMap().get( 351 KeyStroke.getKeyStrokeForEvent(evt)); 352 353 if (actionMapKey != null) { 354 Action action = completionScrollPane.getActionMap().get(actionMapKey); 355 if (action != null) { 356 action.actionPerformed(new ActionEvent (completionScrollPane, 0, null)); 357 evt.consume(); 358 } 359 } 360 } 361 } 362 363 protected int getAnchorHorizontalShift() { 364 return COMPLETION_ANCHOR_HORIZONTAL_SHIFT; 365 } 366 367 } 368 369 private static final class DocPopup extends CompletionLayoutPopup { 370 371 private DocumentationScrollPane getDocumentationScrollPane() { 372 return (DocumentationScrollPane)getContentComponent(); 373 } 374 375 protected void show(CompletionDocumentation doc, int anchorOffset) { 376 JTextComponent editorComponent = getEditorComponent(); 377 if (editorComponent == null) { 378 return; 379 } 380 381 if (!isVisible()) { setContentComponent(new DocumentationScrollPane(editorComponent)); 383 } 384 385 getDocumentationScrollPane().setData(doc); 386 387 if (!isVisible()) { setAnchorOffset(anchorOffset); 391 getLayout().updateLayout(this); 392 } } 394 395 public void processKeyEvent(KeyEvent evt) { 396 if (isVisible()) { 397 Object actionMapKey = getDocumentationScrollPane().getInputMap().get( 398 KeyStroke.getKeyStrokeForEvent(evt)); 399 400 if (actionMapKey != null) { 401 Action action = getDocumentationScrollPane().getActionMap().get(actionMapKey); 402 if (action != null) { 403 action.actionPerformed(new ActionEvent (getDocumentationScrollPane(), 0, null)); 404 evt.consume(); 405 } 406 } 407 } 408 } 409 410 public void clearHistory() { 411 if (isVisible()) { 412 getDocumentationScrollPane().clearHistory(); 413 } 414 } 415 416 protected int getAnchorHorizontalShift() { 417 return COMPLETION_ANCHOR_HORIZONTAL_SHIFT; 418 } 419 420 } 421 422 private static final class TipPopup extends CompletionLayoutPopup { 423 424 protected void show(JToolTip toolTip, int anchorOffset) { 425 JComponent lastComponent = null; 426 if (isVisible()) { lastComponent = getContentComponent(); 428 } 429 430 setContentComponent(toolTip); 431 setAnchorOffset(anchorOffset); 432 433 if (lastComponent != toolTip) { 436 getLayout().updateLayout(this); 437 } 438 } 439 440 public void processKeyEvent(KeyEvent evt) { 441 if (isVisible()) { 442 if (KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0).equals( 443 KeyStroke.getKeyStrokeForEvent(evt)) 444 ) { 445 evt.consume(); 446 CompletionImpl.get().hideToolTip(); 447 } 448 } 449 } 450 451 } 452 453 } 454 | Popular Tags |