1 19 20 package org.netbeans.modules.editor.completion; 21 22 import java.awt.Dimension ; 23 import java.awt.GraphicsConfiguration ; 24 import java.awt.Point ; 25 import java.awt.Rectangle ; 26 import java.awt.event.KeyEvent ; 27 import javax.swing.JComponent ; 28 import javax.swing.Popup ; 29 import javax.swing.PopupFactory ; 30 import javax.swing.SwingUtilities ; 31 import javax.swing.text.BadLocationException ; 32 import javax.swing.text.JTextComponent ; 33 34 35 41 abstract class CompletionLayoutPopup { 42 43 private CompletionLayout layout; 44 45 private Popup popup; 46 47 48 private Rectangle popupBounds; 49 50 private JComponent contentComponent; 51 52 private int anchorOffset; 53 54 private Rectangle anchorOffsetBounds; 55 56 private boolean displayAboveCaret; 57 58 private Rectangle screenBounds; 59 60 private boolean preferDisplayAboveCaret; 61 62 private boolean showRetainedPreferredSize; 63 64 public final boolean isVisible() { 65 return (popup != null); 66 } 67 68 public final boolean isActive() { 69 return (contentComponent != null); 70 } 71 72 public final void hide() { 73 if (isVisible()) { 74 popup.hide(); 75 popup = null; 76 popupBounds = null; 77 contentComponent = null; 78 anchorOffset = -1; 79 screenBounds = null; 81 } 82 } 83 84 public final boolean isDisplayAboveCaret() { 85 return displayAboveCaret; 86 } 87 88 public final Rectangle getPopupBounds() { 89 return popupBounds; 90 } 91 92 final void setLayout(CompletionLayout layout) { 93 assert (layout != null); 94 this.layout = layout; 95 } 96 97 final void setPreferDisplayAboveCaret(boolean preferDisplayAboveCaret) { 98 this.preferDisplayAboveCaret = preferDisplayAboveCaret; 99 } 100 101 final void setContentComponent(JComponent contentComponent) { 102 assert (contentComponent != null); 103 this.contentComponent = contentComponent; 104 } 105 106 final void setAnchorOffset(int anchorOffset) { 107 this.anchorOffset = anchorOffset; 108 anchorOffsetBounds = null; 109 } 110 111 final Rectangle getScreenBounds() { 112 if (screenBounds == null) { 113 JTextComponent editorComponent = getEditorComponent(); 114 GraphicsConfiguration configuration = editorComponent != null 115 ? editorComponent.getGraphicsConfiguration() : null; 116 screenBounds = configuration != null 117 ? configuration.getBounds() : new Rectangle (); 118 } 119 return screenBounds; 120 } 121 122 final int getAnchorOffset() { 123 int offset = anchorOffset; 124 if (offset == -1) { 125 JTextComponent editorComponent = getEditorComponent(); 127 if (editorComponent != null) { 128 offset = editorComponent.getSelectionStart(); 129 } 130 } 131 return offset; 132 } 133 134 final JComponent getContentComponent() { 135 return contentComponent; 136 } 137 138 final Dimension getPreferredSize() { 139 JComponent comp = getContentComponent(); 140 return (comp == null) ? new Dimension (0,0) : comp.getPreferredSize(); 141 } 142 143 final void resetPreferredSize() { 144 JComponent comp = getContentComponent(); 145 if (comp == null){ 146 return; 147 } 148 comp.setPreferredSize(null); 149 } 150 151 final boolean isShowRetainedPreferredSize() { 152 return showRetainedPreferredSize; 153 } 154 155 final CompletionLayout getLayout() { 156 return layout; 157 } 158 159 final JTextComponent getEditorComponent() { 160 return layout.getEditorComponent(); 161 } 162 163 protected int getAnchorHorizontalShift() { 164 return 0; 165 } 166 167 final Rectangle getAnchorOffsetBounds() { 168 JTextComponent editorComponent = getEditorComponent(); 169 if (editorComponent == null) { 170 return new Rectangle (); 171 } 172 if (anchorOffsetBounds == null){ 173 int anchorOffset = getAnchorOffset(); 174 try { 175 anchorOffsetBounds = editorComponent.modelToView(anchorOffset); 176 if (anchorOffsetBounds != null){ 177 anchorOffsetBounds.x -= getAnchorHorizontalShift(); 178 } else { 179 anchorOffsetBounds = new Rectangle (); } 181 } catch (BadLocationException e) { 182 anchorOffsetBounds = new Rectangle (); } 184 Point anchorOffsetPoint = anchorOffsetBounds.getLocation(); 185 SwingUtilities.convertPointToScreen(anchorOffsetPoint, editorComponent); 186 anchorOffsetBounds.setLocation(anchorOffsetPoint); 187 } 188 return anchorOffsetBounds; 189 } 190 191 final Popup getPopup() { 192 return popup; 193 } 194 195 206 private Rectangle findPopupBounds(Rectangle occupiedBounds, boolean aboveOccupiedBounds) { 207 Dimension prefSize = getPreferredSize(); 208 Rectangle screen = getScreenBounds(); 209 Rectangle popupBounds = new Rectangle (); 210 211 popupBounds.x = Math.min(occupiedBounds.x, 212 (screen.x + screen.width) - prefSize.width); 213 popupBounds.x = Math.max(popupBounds.x, screen.x); 214 popupBounds.width = Math.min(prefSize.width, screen.width); 215 216 if (aboveOccupiedBounds) { 217 popupBounds.height = Math.min(prefSize.height, 218 occupiedBounds.y - screen.y - CompletionLayout.POPUP_VERTICAL_GAP); 219 popupBounds.y = occupiedBounds.y - CompletionLayout.POPUP_VERTICAL_GAP - popupBounds.height; 220 } else { popupBounds.y = occupiedBounds.y 222 + occupiedBounds.height + CompletionLayout.POPUP_VERTICAL_GAP; 223 popupBounds.height = Math.min(prefSize.height, 224 (screen.y + screen.height) - popupBounds.y); 225 } 226 return popupBounds; 227 } 228 229 236 private void show(Rectangle popupBounds, boolean displayAboveCaret) { 237 if (popup != null) { 239 popup.hide(); 240 popup = null; 241 } 242 243 Dimension origPrefSize = getPreferredSize(); 245 Dimension newPrefSize = popupBounds.getSize(); 246 JComponent contComp = getContentComponent(); 247 if (contComp == null){ 248 return; 249 } 250 contComp.setPreferredSize(newPrefSize); 251 showRetainedPreferredSize = newPrefSize.equals(origPrefSize); 252 253 PopupFactory factory = PopupFactory.getSharedInstance(); 254 popup = factory.getPopup(layout.getEditorComponent(), contComp, 255 popupBounds.x, popupBounds.y); 256 popup.show(); 257 258 this.popupBounds = popupBounds; 259 this.displayAboveCaret = displayAboveCaret; 260 } 261 262 266 void showAlongAnchorBounds() { 267 showAlongOccupiedBounds(getAnchorOffsetBounds()); 268 } 269 270 void showAlongAnchorBounds(boolean aboveCaret) { 271 showAlongOccupiedBounds(getAnchorOffsetBounds(), aboveCaret); 272 } 273 274 278 void showAlongOccupiedBounds(Rectangle occupiedBounds) { 279 boolean aboveCaret; 280 if (isEnoughSpace(occupiedBounds, preferDisplayAboveCaret)) { 281 aboveCaret = preferDisplayAboveCaret; 282 } else { aboveCaret = isMoreSpaceAbove(occupiedBounds); 285 } 286 Rectangle bounds = findPopupBounds(occupiedBounds, aboveCaret); 287 show(bounds, aboveCaret); 288 } 289 290 void showAlongOccupiedBounds(Rectangle occupiedBounds, boolean aboveCaret) { 291 Rectangle bounds = findPopupBounds(occupiedBounds, aboveCaret); 292 show(bounds, aboveCaret); 293 } 294 295 boolean isMoreSpaceAbove(Rectangle bounds) { 296 Rectangle screen = getScreenBounds(); 297 int above = bounds.y - screen.y; 298 int below = (screen.y + screen.height) - (bounds.y + bounds.height); 299 return (above > below); 300 } 301 302 306 boolean isEnoughSpace(Rectangle occupiedBounds) { 307 return isEnoughSpace(occupiedBounds, preferDisplayAboveCaret); 308 } 309 310 321 boolean isEnoughSpace(Rectangle occupiedBounds, boolean aboveOccupiedBounds) { 322 Rectangle screen = getScreenBounds(); 323 int freeHeight = aboveOccupiedBounds 324 ? occupiedBounds.y - screen.y 325 : (screen.y + screen.height) - (occupiedBounds.y + occupiedBounds.height); 326 Dimension prefSize = getPreferredSize(); 327 return (prefSize.height < freeHeight); 328 } 329 330 boolean isEnoughSpace(boolean aboveCaret) { 331 return isEnoughSpace(getAnchorOffsetBounds(), aboveCaret); 332 } 333 334 public boolean isOverlapped(Rectangle bounds) { 335 return isVisible() ? popupBounds.intersects(bounds) : false; 336 } 337 338 public boolean isOverlapped(CompletionLayoutPopup popup) { 339 return popup.isVisible() ? isOverlapped(popup.getPopupBounds()) : false; 340 } 341 342 public Rectangle unionBounds(Rectangle bounds) { 343 return isVisible() ? bounds.union(getPopupBounds()) : bounds; 344 } 345 346 public abstract void processKeyEvent(KeyEvent evt); 347 } 348 | Popular Tags |