1 19 20 package org.netbeans.modules.xml.text.navigator; 21 22 import java.awt.BorderLayout ; 23 import java.awt.Color ; 24 import java.awt.Container ; 25 import java.awt.Graphics ; 26 import java.awt.Toolkit ; 27 import java.awt.event.ActionEvent ; 28 import java.awt.event.KeyEvent ; 29 import java.awt.event.MouseAdapter ; 30 import java.awt.event.MouseEvent ; 31 import java.awt.event.MouseListener ; 32 import java.beans.PropertyChangeEvent ; 33 import java.beans.PropertyChangeListener ; 34 import java.io.IOException ; 35 import java.lang.ref.WeakReference ; 36 import java.lang.reflect.InvocationTargetException ; 37 import java.util.WeakHashMap ; 38 import javax.swing.AbstractAction ; 39 import javax.swing.BorderFactory ; 40 import javax.swing.Icon ; 41 import javax.swing.ImageIcon ; 42 import javax.swing.JEditorPane ; 43 import javax.swing.JLabel ; 44 import javax.swing.JMenuItem ; 45 import javax.swing.JPanel ; 46 import javax.swing.JPopupMenu ; 47 import javax.swing.JScrollPane ; 48 import javax.swing.JTree ; 49 import javax.swing.KeyStroke ; 50 import javax.swing.SwingConstants ; 51 import javax.swing.SwingUtilities ; 52 import javax.swing.ToolTipManager ; 53 import javax.swing.border.EmptyBorder ; 54 import javax.swing.event.ChangeEvent ; 55 import javax.swing.text.Document ; 56 import javax.swing.text.JTextComponent ; 57 import javax.swing.tree.DefaultTreeModel ; 58 import javax.swing.tree.DefaultTreeSelectionModel ; 59 import javax.swing.tree.TreeModel ; 60 import javax.swing.tree.TreeNode ; 61 import javax.swing.tree.TreePath ; 62 import javax.swing.tree.TreeSelectionModel ; 63 import org.netbeans.editor.BaseDocument; 64 import org.netbeans.editor.EditorUI; 65 import org.netbeans.editor.ext.ExtEditorUI; 66 import org.netbeans.modules.editor.NbEditorUtilities; 67 import org.netbeans.modules.editor.structure.api.DocumentElement; 68 import org.netbeans.modules.editor.structure.api.DocumentModel; 69 import org.netbeans.modules.editor.structure.api.DocumentModelException; 70 import org.openide.ErrorManager; 71 import org.openide.cookies.EditorCookie; 72 import org.openide.cookies.EditorCookie.Observable; 73 import org.openide.loaders.DataObject; 74 import org.openide.nodes.Node; 75 import org.openide.text.CloneableEditorSupport; 76 import org.openide.text.DataEditorSupport; 77 import org.openide.util.Lookup.Template; 78 import org.openide.util.NbBundle; 79 import org.openide.util.RequestProcessor; 80 import org.openide.util.UserQuestionException; 81 import org.openide.util.Utilities; 82 import org.openide.windows.TopComponent; 83 84 85 90 public class NavigatorContent extends JPanel implements PropertyChangeListener { 91 92 private static final boolean DEBUG = false; 93 private static NavigatorContent navigatorContentInstance = null; 94 95 96 public static synchronized NavigatorContent getDefault() { 97 if(navigatorContentInstance == null) 98 navigatorContentInstance = new NavigatorContent(); 99 return navigatorContentInstance; 100 } 101 102 static boolean showAttributes = true; 106 static boolean showContent = true; 107 108 private JPanel active = null; 109 private final JPanel emptyPanel; 110 111 private JLabel msgLabel; 112 113 private DataObject peerDO = null; 114 115 private WeakHashMap uiCache = new WeakHashMap (); 116 117 private boolean editorOpened = false; 118 119 private Icon waitIcon; 120 121 private NavigatorContent() { 122 setLayout(new BorderLayout ()); 123 setBackground(Color.WHITE); 125 emptyPanel = new JPanel (); 126 emptyPanel.setBackground(Color.WHITE); 127 emptyPanel.setLayout(new BorderLayout ()); 128 msgLabel = new JLabel (); 129 emptyPanel.add(msgLabel, BorderLayout.CENTER); 130 } 131 132 public void navigate(DataObject d) { 133 if(peerDO != null && peerDO != d) { 134 closeDocument(peerDO); 136 } 137 138 EditorCookie ec = (EditorCookie)d.getCookie(EditorCookie.class); 139 if(ec == null) { 140 ErrorManager.getDefault().log(ErrorManager.INFORMATIONAL, "The DataObject " + d.getName() + "(class=" + d.getClass().getName() + ") has no EditorCookie!?"); 141 } else { 142 try { 143 if(DEBUG) System.out.println("[xml navigator] navigating to DATAOBJECT " + d.hashCode()); 144 BaseDocument bdoc = (BaseDocument)ec.openDocument(); 146 if(bdoc != null) { 148 navigate(d, bdoc); 150 this.peerDO = d; 152 editorOpened = ec.getOpenedPanes() != null && ec.getOpenedPanes().length > 0; 154 } 155 156 }catch(UserQuestionException uqe) { 157 showDocumentTooLarge(); 159 }catch(IOException e) { 160 ErrorManager.getDefault().notify(e); 161 } 162 } 163 } 164 165 public void navigate(final DataObject documentDO, final BaseDocument bdoc) { 166 if(DEBUG) System.out.println("[xml navigator] navigating to DOCUMENT " + bdoc.hashCode()); 167 showScanningPanel(); 169 170 final JPanel cachedPanel; 172 WeakReference panelWR = (WeakReference )uiCache.get(documentDO); 173 if(panelWR != null) { 174 NavigatorContentPanel cp = (NavigatorContentPanel)panelWR.get(); 175 if(cp != null) { 176 if(DEBUG) System.out.println("panel is cached"); 177 cachedPanel = bdoc == cp.getDocument() ? cp : null; 179 if(cachedPanel == null) { 180 if(DEBUG) System.out.println("but the document is different - creating a new UI..."); 181 if(DEBUG) System.out.println("the cached document : " + cp.getDocument()); 182 183 uiCache.remove(documentDO); 185 } 186 } else 187 cachedPanel = null; 188 } else 189 cachedPanel = null; 190 191 RequestProcessor.getDefault().post(new Runnable () { 193 public void run() { 194 try { 196 final DocumentModel model; 197 if(cachedPanel == null) 198 model = DocumentModel.getDocumentModel(bdoc); 199 else 200 model = null; 202 203 if(cachedPanel != null || model != null) { 204 205 try { 206 SwingUtilities.invokeAndWait(new Runnable () { 207 public void run() { 208 showWaitPanel(); 209 JPanel panel = null; 210 if(cachedPanel == null) { 211 try { 212 model.readLock(); 215 216 panel = new NavigatorContentPanel(model); 218 uiCache.put(documentDO, new WeakReference (panel)); 221 if(DEBUG) System.out.println("[xml navigator] panel created"); 222 223 EditorCookie.Observable eco = (EditorCookie.Observable)documentDO.getCookie(EditorCookie.Observable.class); 225 if(eco != null) { 226 eco.addPropertyChangeListener(NavigatorContent.this); 227 } else { 228 ErrorManager.getDefault().log(ErrorManager.INFORMATIONAL, "The DataObject " + documentDO.getName() + "(class=" + documentDO.getClass().getName() + ") has no EditorCookie.Observable!"); 229 } 230 }finally{ 231 model.readUnlock(); 233 } 234 } else { 235 panel = cachedPanel; 236 if(DEBUG) System.out.println("[xml navigator] panel gotten from cache"); 237 } 238 239 removeAll(); 241 add(panel, BorderLayout.CENTER); 242 revalidate(); 243 repaint(); 245 } 246 }); 247 }catch(InterruptedException ie) { 248 ErrorManager.getDefault().notify(ErrorManager.WARNING, ie); 249 }catch(InvocationTargetException ite) { 250 ErrorManager.getDefault().notify(ErrorManager.ERROR, ite); 251 } 252 } else { 253 showCannotNavigate(); 255 } 256 }catch(DocumentModelException dme) { 257 ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, dme); 258 } 259 } 260 }); 261 } 262 263 public void release() { 264 removeAll(); 265 repaint(); 266 267 closeDocument(peerDO); 268 } 269 270 273 private void closeDocument(DataObject dobj) { 274 if(dobj != null) { 275 EditorCookie ec = (EditorCookie)peerDO.getCookie(EditorCookie.class); 276 if(ec != null) { 277 JEditorPane panes[] = ec.getOpenedPanes(); 278 if((panes == null || panes.length == 0)) { 280 ((EditorCookie.Observable)ec).removePropertyChangeListener(this); 281 282 if(editorOpened) { 283 ec.close(); 284 if(DEBUG) System.out.println("document instance for dataobject " + dobj.hashCode() + " closed."); 285 } 286 } 287 editorOpened = false; 288 } 289 } 290 } 291 292 public void showDocumentTooLarge() { 293 removeAll(); 294 msgLabel.setForeground(Color.GRAY); 295 msgLabel.setText(NbBundle.getMessage(NavigatorContent.class, "LBL_TooLarge")); 296 msgLabel.setHorizontalAlignment(SwingConstants.CENTER); 297 add(emptyPanel, BorderLayout.CENTER); 298 repaint(); 299 } 300 301 public void showCannotNavigate() { 302 removeAll(); 303 msgLabel.setIcon(null); 304 msgLabel.setForeground(Color.GRAY); 305 msgLabel.setText(NbBundle.getMessage(NavigatorContent.class, "LBL_CannotNavigate")); 306 msgLabel.setHorizontalAlignment(SwingConstants.CENTER); 307 add(emptyPanel, BorderLayout.CENTER); 308 repaint(); 309 } 310 311 private void showScanningPanel() { 312 removeAll(); 313 if (waitIcon == null) { 314 waitIcon = new ImageIcon ( Utilities.loadImage( 315 "org/netbeans/modules/xml/text/navigator/resources/wait.gif" ) ); } 317 msgLabel.setIcon(waitIcon); 318 msgLabel.setHorizontalAlignment(SwingConstants.LEFT); 319 msgLabel.setForeground(Color.BLACK); 320 msgLabel.setText(NbBundle.getMessage(NavigatorContent.class, "LBL_Scan")); 321 add(emptyPanel, BorderLayout.NORTH); 322 repaint(); 323 } 324 325 326 private void showWaitPanel() { 327 removeAll(); 328 msgLabel.setIcon(null); 329 msgLabel.setForeground(Color.GRAY); 330 msgLabel.setHorizontalAlignment(SwingConstants.LEFT); 331 msgLabel.setText(NbBundle.getMessage(NavigatorContent.class, "LBL_Wait")); 332 add(emptyPanel, BorderLayout.NORTH); 333 repaint(); 334 } 335 336 public void propertyChange(PropertyChangeEvent evt) { 337 if(evt.getPropertyName() == EditorCookie.Observable.PROP_DOCUMENT) { 338 if(evt.getNewValue() == null) { 339 final DataObject dobj = ((DataEditorSupport)evt.getSource()).getDataObject(); 340 if(dobj != null) { 341 editorOpened = false; 342 if(DEBUG) System.out.println("document has been closed for DO: " + dobj.hashCode()); 344 345 EditorCookie ec = (EditorCookie)dobj.getCookie(EditorCookie.class); 347 if(ec != null) 348 ((EditorCookie.Observable)ec).removePropertyChangeListener(this); 349 350 SwingUtilities.invokeLater(new Runnable () { 353 public void run() { 354 if(dobj.isValid()) navigate(dobj); 355 } 356 }); 357 } 358 } else { 359 editorOpened = true; 361 } 362 } 363 } 364 365 private class NavigatorContentPanel extends JPanel implements FiltersManager.FilterChangeListener { 366 367 private JTree tree; 368 private FiltersManager filters; 369 private Document doc; 370 371 public NavigatorContentPanel(DocumentModel dm) { 372 this.doc = dm.getDocument(); 373 374 setLayout(new BorderLayout ()); 375 tree = new PatchedJTree(); 377 TreeModel model = createTreeModel(dm); 378 tree.setModel(model); 379 tree.setShowsRootHandles(true); 381 tree.setRootVisible(false); 382 tree.setCellRenderer(new NavigatorTreeCellRenderer()); 383 tree.putClientProperty("JTree.lineStyle", "Angled"); 384 ToolTipManager.sharedInstance().registerComponent(tree); 385 386 MouseListener ml = new MouseAdapter () { 387 public void mousePressed(MouseEvent e) { 388 int selRow = tree.getRowForLocation(e.getX(), e.getY()); 389 if(selRow != -1) { 390 TreePath selPath = tree.getPathForLocation(e.getX(), e.getY()); 391 TreeNodeAdapter tna = (TreeNodeAdapter)selPath.getLastPathComponent(); 392 if(e.getClickCount() == 2) 393 openAndFocusElement(tna, false); 394 395 if(e.getClickCount() == 1) 396 openAndFocusElement(tna, true); 398 } 399 } 400 }; 401 tree.addMouseListener(ml); 402 403 final TreeSelectionModel selectionModel = new DefaultTreeSelectionModel (); 404 selectionModel.setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION); 405 tree.setSelectionModel(selectionModel); 406 tree.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), "open"); tree.getActionMap().put("open", new AbstractAction () { public void actionPerformed(ActionEvent e) { 409 TreePath selPath = selectionModel.getLeadSelectionPath(); 410 TreeNodeAdapter tna = (TreeNodeAdapter)selPath.getLastPathComponent(); 411 openAndFocusElement(tna, false); 412 } 413 }); 414 415 JScrollPane treeView = new JScrollPane (tree); 416 treeView.setBorder(BorderFactory.createEmptyBorder()); 417 treeView.setViewportBorder(BorderFactory.createEmptyBorder()); 418 419 add(treeView, BorderLayout.CENTER); 420 421 TapPanel filtersPanel = new TapPanel(); 423 JLabel filtersLbl = new JLabel (NbBundle.getMessage(NavigatorContent.class, "LBL_Filter")); filtersLbl.setBorder(new EmptyBorder (0, 5, 5, 0)); 425 filtersPanel.add(filtersLbl); 426 filtersPanel.setOrientation(TapPanel.DOWN); 427 KeyStroke toggleKey = KeyStroke.getKeyStroke(KeyEvent.VK_T, 429 Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()); 430 String keyText = org.openide.util.Utilities.keyToString(toggleKey); 431 filtersPanel.setToolTipText(NbBundle.getMessage(NavigatorContent.class, "TIP_TapPanel", keyText)); 432 433 filters = createFilters(); 435 filters.hookChangeListener(this); 437 438 filtersPanel.add(filters.getComponent()); 439 440 add(filtersPanel, BorderLayout.SOUTH); 441 442 MouseListener pmml = new MouseAdapter () { 444 public void mousePressed(MouseEvent e) { 445 if(e.getClickCount() == 1 && e.getModifiers() == MouseEvent.BUTTON3_MASK) { 446 JPopupMenu pm = new JPopupMenu (); 448 JMenuItem [] items = new FilterActions(filters).createMenuItems(); 449 for(int i = 0; i < items.length; i++) pm.add(items[i]); 451 pm.pack(); 452 pm.show(tree, e.getX(), e.getY()); 453 } 454 } 455 }; 456 tree.addMouseListener(pmml); 457 458 TreeNode rootNode = (TreeNode )model.getRoot(); 460 for(int i = 0; i < rootNode.getChildCount(); i++) { 461 TreeNode node = rootNode.getChildAt(i); 462 if(node.getChildCount() > 0) 463 tree.expandPath(new TreePath (new TreeNode []{rootNode, node})); 464 } 465 } 466 467 public Document getDocument() { 468 return this.doc; 469 } 470 471 private void openAndFocusElement(final TreeNodeAdapter selected, final boolean selectLineOnly) { 472 BaseDocument bdoc = (BaseDocument)selected.getDocumentElement().getDocument(); 473 DataObject dobj = NbEditorUtilities.getDataObject(bdoc); 474 if(dobj == null) return ; 475 476 final EditorCookie.Observable ec = (EditorCookie.Observable)dobj.getCookie(EditorCookie.Observable.class); 477 if(ec == null) return ; 478 479 SwingUtilities.invokeLater(new Runnable () { 480 public void run() { 481 JEditorPane [] panes = ec.getOpenedPanes(); 482 if (panes != null && panes.length > 0) { 483 selectElementInPane(panes[0], selected, !selectLineOnly); 485 } else if(!selectLineOnly) { 486 ec.open(); 488 try { 489 ec.openDocument(); panes = ec.getOpenedPanes(); 491 if (panes != null && panes.length > 0) { 492 selectElementInPane(panes[0], selected, true); 493 } 494 }catch(IOException ioe) { 495 ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, ioe); 496 } 497 } 498 } 499 }); 500 } 501 502 private void selectElementInPane(final JEditorPane pane, final TreeNodeAdapter tna, final boolean focus) { 503 RequestProcessor.getDefault().post(new Runnable () { 504 public void run() { 505 pane.setCaretPosition(tna.getDocumentElement().getStartOffset()); 506 } 507 }); 508 if(focus) { 509 Container temp = pane; 511 while (!(temp instanceof TopComponent)) { 512 temp = temp.getParent(); 513 } 514 ((TopComponent) temp).requestActive(); 515 } 516 } 517 518 private TreeModel createTreeModel(DocumentModel dm) { 519 DocumentElement rootElement = dm.getRootElement(); 520 DefaultTreeModel dtm = new DefaultTreeModel (null); 521 TreeNodeAdapter rootTna = new TreeNodeAdapter(rootElement, dtm, tree, null); 522 dtm.setRoot(rootTna); 523 524 return dtm; 525 } 526 527 528 private FiltersManager createFilters() { 529 FiltersDescription desc = new FiltersDescription(); 530 531 desc.addFilter(ATTRIBUTES_FILTER, 532 NbBundle.getMessage(NavigatorContent.class, "LBL_ShowAttributes"), NbBundle.getMessage(NavigatorContent.class, "LBL_ShowAttributesTip"), showAttributes, 535 new ImageIcon (org.openide.util.Utilities.loadImage("org/netbeans/modules/xml/text/navigator/resources/a.png")), null 537 ); 538 desc.addFilter(CONTENT_FILTER, 539 NbBundle.getMessage(NavigatorContent.class, "LBL_ShowContent"), NbBundle.getMessage(NavigatorContent.class, "LBL_ShowContentTip"), showContent, 542 new ImageIcon (org.openide.util.Utilities.loadImage("org/netbeans/modules/xml/text/navigator/resources/content.png")), null 544 ); 545 546 return FiltersDescription.createManager(desc); 547 } 548 549 550 public void filterStateChanged(ChangeEvent e) { 551 showAttributes = filters.isSelected(ATTRIBUTES_FILTER); 552 showContent = filters.isSelected(CONTENT_FILTER); 553 554 tree.repaint(); 555 } 556 557 private class PatchedJTree extends JTree { 558 559 private boolean firstPaint; 560 561 public PatchedJTree() { 562 super(); 563 firstPaint = true; 564 } 565 566 567 public void paint(Graphics g) { 568 if (firstPaint) { 569 int height = g.getFontMetrics(getFont()).getHeight(); 570 setRowHeight(height + 2); 571 firstPaint = false; 572 } 573 super.paint(g); 574 } 575 576 } 577 578 public static final String ATTRIBUTES_FILTER = "attrs"; 579 public static final String CONTENT_FILTER = "content"; 580 581 } 582 583 } 584 585 | Popular Tags |