1 56 package org.objectstyle.cayenne.modeler.util; 57 58 import java.awt.BorderLayout ; 59 import java.awt.Component ; 60 import java.awt.Dimension ; 61 import java.awt.GridLayout ; 62 import java.awt.Rectangle ; 63 import java.io.Serializable ; 64 import java.util.ArrayList ; 65 import java.util.Collections ; 66 import java.util.Iterator ; 67 import java.util.List ; 68 69 import javax.swing.AbstractListModel ; 70 import javax.swing.DefaultListCellRenderer ; 71 import javax.swing.ImageIcon ; 72 import javax.swing.JLabel ; 73 import javax.swing.JList ; 74 import javax.swing.JPanel ; 75 import javax.swing.JScrollPane ; 76 import javax.swing.JViewport ; 77 import javax.swing.ListCellRenderer ; 78 import javax.swing.ListSelectionModel ; 79 import javax.swing.border.Border ; 80 import javax.swing.event.ListSelectionEvent ; 81 import javax.swing.event.ListSelectionListener ; 82 import javax.swing.event.TreeSelectionEvent ; 83 import javax.swing.event.TreeSelectionListener ; 84 import javax.swing.tree.TreeModel ; 85 import javax.swing.tree.TreePath ; 86 87 88 109 public class MultiColumnBrowser extends JPanel { 110 111 private static final ImageIcon rightArrow = 112 ModelerUtil.buildIcon("scroll_right.gif"); 113 114 public static final int DEFAULT_MIN_COLUMNS_COUNT = 3; 115 116 protected int minColumns; 117 protected ListCellRenderer renderer; 118 protected TreeModel model; 119 protected Object [] selectionPath; 120 protected Dimension preferredColumnSize; 121 122 private List columns; 123 private ListSelectionListener browserSelector; 124 private List treeSelectionListeners; 125 126 public MultiColumnBrowser() { 127 this(DEFAULT_MIN_COLUMNS_COUNT); 128 } 129 130 public MultiColumnBrowser(int minColumns) { 131 if (minColumns < DEFAULT_MIN_COLUMNS_COUNT) { 132 throw new IllegalArgumentException ( 133 "Expected " 134 + DEFAULT_MIN_COLUMNS_COUNT 135 + " or more columns, got: " 136 + minColumns); 137 } 138 139 this.minColumns = minColumns; 140 this.browserSelector = new PanelController(); 141 this.treeSelectionListeners = Collections.synchronizedList(new ArrayList ()); 142 initView(); 143 } 144 145 public void addTreeSelectionListener(TreeSelectionListener listener) { 146 synchronized (treeSelectionListeners) { 147 if (listener != null && !treeSelectionListeners.contains(listener)) { 148 treeSelectionListeners.add(listener); 149 } 150 } 151 } 152 153 public void removeTreeSelectionListener(TreeSelectionListener listener) { 154 synchronized (treeSelectionListeners) { 155 treeSelectionListeners.remove(listener); 156 } 157 } 158 159 162 protected void fireTreeSelectionEvent(Object [] selectionPath) { 163 TreeSelectionEvent e = 164 new TreeSelectionEvent (this, new TreePath (selectionPath), false, null, null); 165 synchronized (treeSelectionListeners) { 166 Iterator it = treeSelectionListeners.iterator(); 167 while (it.hasNext()) { 168 TreeSelectionListener listener = (TreeSelectionListener ) it.next(); 169 listener.valueChanged(e); 170 } 171 } 172 } 173 174 177 public TreePath getSelectionPath() { 178 return new TreePath (selectionPath); 179 } 180 181 184 public int getMinColumns() { 185 return minColumns; 186 } 187 188 191 public void setMinColumns(int minColumns) { 192 this.minColumns = minColumns; 193 } 194 195 198 public Dimension getPreferredColumnSize() { 199 return preferredColumnSize; 200 } 201 202 public void setPreferredColumnSize(Dimension preferredColumnSize) { 203 this.preferredColumnSize = preferredColumnSize; 204 refreshPreferredSize(); 205 } 206 207 212 public void setDefaultRenderer() { 213 if (!(renderer instanceof MultiColumnBrowserRenderer)) { 214 setRenderer(new MultiColumnBrowserRenderer()); 215 } 216 } 217 218 221 public ListCellRenderer getRenderer() { 222 return renderer; 223 } 224 225 228 public synchronized void setRenderer(ListCellRenderer renderer) { 229 if (this.renderer != renderer) { 230 this.renderer = renderer; 231 232 if (columns != null && columns.size() > 0) { 234 Iterator it = columns.iterator(); 235 while (it.hasNext()) { 236 JList column = (JList ) it.next(); 237 column.setCellRenderer(renderer); 238 } 239 } 240 } 241 } 242 243 246 public synchronized void setModel(TreeModel model) { 247 if (model == null) { 248 throw new NullPointerException ("Null tree model."); 249 } 250 251 if (this.model != model) { 252 this.model = model; 253 254 updateFromModel(model.getRoot(), -1); 256 } 257 } 258 259 262 public TreeModel getModel() { 263 return model; 264 } 265 266 269 public int getColumnsCount() { 270 return columns.size(); 271 } 272 273 277 private void initView() { 278 columns = Collections.synchronizedList(new ArrayList (minColumns)); 279 adjustViewColumns(minColumns); 280 } 281 282 286 private void adjustViewColumns(int delta) { 287 if (delta == 0) { 288 return; 289 } 290 291 setLayout(new GridLayout (1, columns.size() + delta, 3, 3)); 292 if (delta > 0) { 293 for (int i = 0; i < delta; i++) { 294 appendColumn(); 295 } 296 } 297 else { 298 for (int i = -delta; i > 0 && columns.size() > minColumns; i--) { 299 removeLastColumn(); 300 } 301 } 302 303 refreshPreferredSize(); 304 revalidate(); 305 } 306 307 private BrowserPanel appendColumn() { 308 BrowserPanel panel = new BrowserPanel(); 309 panel.addListSelectionListener(browserSelector); 310 panel.setCellRenderer(renderer); 311 312 columns.add(panel); 313 JScrollPane scroller = 314 new JScrollPane ( 315 panel, 316 JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, 317 JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); 318 319 if (preferredColumnSize != null) { 323 scroller.setPreferredSize(preferredColumnSize); 324 } 325 add(scroller); 326 return panel; 327 } 328 329 private BrowserPanel removeLastColumn() { 330 if (columns.size() == 0) { 331 return null; 332 } 333 334 int index = columns.size() - 1; 335 336 BrowserPanel panel = (BrowserPanel) columns.remove(index); 337 panel.removeListSelectionListener(browserSelector); 338 339 remove(index); 341 return panel; 342 } 343 344 348 private void refreshPreferredSize() { 349 if (preferredColumnSize != null) { 350 int w = getColumnsCount() * (preferredColumnSize.width + 3) + 3; 351 int h = preferredColumnSize.height + 6; 352 setPreferredSize(new Dimension (w, h)); 353 } 354 } 355 356 359 private void scrollToColumn(int column) { 360 if (getParent() instanceof JViewport ) { 361 362 JViewport viewport = (JViewport ) getParent(); 363 364 double x = getWidth() * column / ((double) getMinColumns()); 367 double y = getHeight() / 2; 368 369 if (preferredColumnSize != null) { 370 x -= preferredColumnSize.width / 2; 371 if (x < 0) { 372 x = 0; 373 } 374 } 375 376 Rectangle rectangle = new Rectangle ((int) x, (int) y, 1, 1); 377 378 viewport.scrollRectToVisible(rectangle); 380 } 381 } 382 383 386 private synchronized void updateFromModel(Object selectedNode, int panelIndex) { 387 if(selectionPath == null) { 388 selectionPath = new Object [0]; 389 } 390 391 int lastIndex = selectionPath.length; 393 394 for (int i = panelIndex + 1; 396 i <= lastIndex && i >= 0 && i < columns.size(); 397 i++) { 398 BrowserPanel column = (BrowserPanel) columns.get(i); 399 column.getSelectionModel().clearSelection(); 400 column.setRootNode(null); 401 } 402 403 this.selectionPath = rebuildPath(selectionPath, selectedNode, panelIndex); 405 406 panelIndex++; 409 410 adjustViewColumns(panelIndex + 1 - columns.size()); 412 413 if (!model.isLeaf(selectedNode)) { 415 BrowserPanel lastPanel = (BrowserPanel) columns.get(panelIndex); 416 lastPanel.setRootNode(selectedNode); 417 scrollToColumn(panelIndex); 418 } 419 420 fireTreeSelectionEvent(selectionPath); 421 } 422 423 428 private Object [] rebuildPath(Object [] path, Object node, int panelIndex) { 429 Object [] newPath = new Object [panelIndex + 2]; 430 System.arraycopy(path, 0, newPath, 0, panelIndex + 1); 431 newPath[panelIndex + 1] = node; 432 return newPath; 433 } 434 435 439 final class ColumnListModel extends AbstractListModel { 445 Object treeNode; 446 int children; 447 448 void setTreeNode(Object treeNode) { 449 int oldChildren = children; 450 this.treeNode = treeNode; 451 this.children = (treeNode != null) ? model.getChildCount(treeNode) : 0; 452 453 super.fireContentsChanged( 455 MultiColumnBrowser.this, 456 0, 457 Math.max(oldChildren, children)); 458 } 459 460 public Object getElementAt(int index) { 461 return model.getChild(treeNode, index); 462 } 463 464 public int getSize() { 465 return children; 466 } 467 } 468 469 final class BrowserPanel extends JList { 473 BrowserPanel() { 474 BrowserPanel.this.setModel(new ColumnListModel()); 475 BrowserPanel.this.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); 476 } 477 478 void setRootNode(Object node) { 479 ((ColumnListModel) BrowserPanel.this.getModel()).setTreeNode(node); 480 } 481 482 Object getTreeNode() { 483 return ((ColumnListModel) BrowserPanel.this.getModel()).treeNode; 484 } 485 } 486 487 final class PanelController implements ListSelectionListener { 491 public void valueChanged(ListSelectionEvent e) { 492 493 if (!e.getValueIsAdjusting()) { 495 BrowserPanel panel = (BrowserPanel) e.getSource(); 496 Object selectedNode = panel.getSelectedValue(); 497 498 if (selectedNode != null) { 500 updateFromModel(selectedNode, columns.indexOf(panel)); 501 } 502 } 503 } 504 } 505 506 final class MultiColumnBrowserRenderer implements ListCellRenderer , Serializable { 513 514 ListCellRenderer leafRenderer; 515 JPanel nonLeafPanel; 516 ListCellRenderer nonLeafTextRenderer; 517 518 MultiColumnBrowserRenderer() { 519 520 leafRenderer = CellRenderers.listRenderer(); 521 522 nonLeafTextRenderer = new DefaultListCellRenderer () { 523 public Border getBorder() { 524 return null; 525 } 526 }; 527 528 nonLeafPanel = new JPanel (); 529 nonLeafPanel.setLayout(new BorderLayout ()); 530 nonLeafPanel.add(new JLabel (rightArrow), BorderLayout.EAST); 531 nonLeafPanel.add((Component ) nonLeafTextRenderer, BorderLayout.WEST); 532 } 533 534 public Component getListCellRendererComponent( 535 JList list, 536 Object value, 537 int index, 538 boolean isSelected, 539 boolean cellHasFocus) { 540 541 if (getModel().isLeaf(value)) { 542 return leafRenderer.getListCellRendererComponent( 543 list, 544 value, 545 index, 546 isSelected, 547 cellHasFocus); 548 } 549 550 Object renderedValue = ModelerUtil.getObjectName(value); 551 if (renderedValue == null) { 552 renderedValue = " "; 554 } 555 556 Component text = 557 nonLeafTextRenderer.getListCellRendererComponent( 558 list, 559 renderedValue, 560 index, 561 isSelected, 562 cellHasFocus); 563 564 nonLeafPanel.setComponentOrientation(text.getComponentOrientation()); 565 nonLeafPanel.setBackground(text.getBackground()); 566 nonLeafPanel.setForeground(text.getForeground()); 567 nonLeafPanel.setEnabled(text.isEnabled()); 568 return nonLeafPanel; 569 } 570 } 571 572 } 573 | Popular Tags |