1 19 package org.openide.explorer.propertysheet; 20 21 import org.openide.nodes.Node; 22 import org.openide.util.NbBundle; 23 24 import java.awt.Color ; 25 import java.awt.Component ; 26 import java.awt.Container ; 27 import java.awt.Insets ; 28 import java.awt.Point ; 29 import java.awt.event.ActionEvent ; 30 import java.awt.event.KeyEvent ; 31 import java.awt.event.MouseEvent ; 32 import java.awt.event.MouseListener ; 33 34 import java.util.Arrays ; 35 36 import javax.swing.AbstractAction ; 37 import javax.swing.BorderFactory ; 38 import javax.swing.JComponent ; 39 import javax.swing.JPanel ; 40 import javax.swing.JScrollPane ; 41 import javax.swing.JSplitPane ; 42 import javax.swing.JTable ; 43 import javax.swing.JViewport ; 44 import javax.swing.KeyStroke ; 45 import javax.swing.SwingUtilities ; 46 import javax.swing.UIManager ; 47 import javax.swing.event.ChangeEvent ; 48 import javax.swing.event.ChangeListener ; 49 import javax.swing.plaf.ViewportUI ; 50 51 import org.netbeans.modules.openide.explorer.TabbedContainerBridge; 52 53 54 83 class PSheet extends JPanel implements MouseListener { 84 public final static int STATE_HAS_DESCRIPTION = 1; 85 public final static int STATE_HAS_TABS = 2; 86 private int addCount = 0; 87 private String description = ""; private String title = ""; private SelectionAndScrollPositionManager manager = new SelectionAndScrollPositionManager(); 90 private boolean adjusting = false; 91 private boolean helpEnabled = true; 92 private boolean marginPainted = !PropUtils.neverMargin; 93 private Color marginColor = UIManager.getColor("controlShadow"); 94 private String emptyString = "THIS IS A BUG"; private Boolean firstSplit = null; 96 private Object [] tabbedContainerObjects = new String [] { "Hello", "World", "This", "Is", "Me" }; 97 private String [] tabbedContainerTitles = new String [] { "Tab 1", "Tab 2", "Tab 3", "Tab 4", "Tab 5" }; 98 private ChangeListener selectionListener = null; 99 100 public PSheet() { 101 getInputMap(WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put( 102 KeyStroke.getKeyStroke(KeyEvent.VK_F10, KeyEvent.SHIFT_DOWN_MASK), "popup" 103 ); 105 getActionMap().put("popup", new PopupAction()); getActionMap().put("PreviousViewAction", new SwitchTabAction(-1)); getActionMap().put("NextViewAction", new SwitchTabAction(1)); } 109 110 SelectionAndScrollPositionManager manager() { 111 return manager; 112 } 113 114 public void adjustForName(String nodeName) { 115 adjusting = true; 116 117 try { 118 JComponent comp = findTabbedContainer(); 119 String tabname = null; 120 121 if (comp != null) { 122 boolean success = false; 123 124 tabname = manager().getLastSelectedGroupName(); 126 if( tabname != null && tabname.length() > 0 ) { 127 success = TabbedContainerBridge.getDefault().setSelectionByName(comp, tabname); 128 } 129 130 if( !success ) { 132 tabname = manager().getGroupNameForNodeName(nodeName); 133 if( tabname != null && tabname.length() > 0 ) { 134 success = TabbedContainerBridge.getDefault().setSelectionByName(comp, tabname); 135 } 136 } 137 138 if( !success ) { 140 tabname = PropUtils.basicPropsTabName(); success = TabbedContainerBridge.getDefault().setSelectionByName(comp, tabname); 142 } 143 144 if (success) { 145 if (selectionListener != null) { 146 ChangeEvent ce = new ChangeEvent (this); 147 selectionListener.stateChanged(ce); 148 } 149 } 150 } 151 152 JScrollPane jsc = findScrollPane(); 153 154 if (jsc != null) { 155 String s = (tabname == null) ? manager().getCurrentNodeName() : tabname; 156 157 if (s != null) { 159 int pos = manager().getScrollPositionForNodeName(s); 160 161 if ((pos >= 0) && (pos < jsc.getVerticalScrollBar().getModel().getMaximum())) { 162 jsc.getVerticalScrollBar().getModel().setValue(pos); 163 } 164 } 165 } 166 } finally { 167 adjusting = false; 168 } 169 } 170 171 public boolean isAdjusting() { 172 return adjusting; 173 } 174 175 public void storeScrollAndTabInfo() { 176 JComponent comp = findTabbedContainer(); 177 String tab = null; 178 String node = manager().getCurrentNodeName(); 179 String lastTab = manager().getLastSelectedGroupName(); 180 181 if (node != null) { 182 if (comp != null) { 183 tab = TabbedContainerBridge.getDefault().getCurrentSelectedTabName(comp); 184 185 if (tab != null) { 186 manager().storeLastSelectedGroup(tab); 187 } 188 } 189 190 JScrollPane jsc = findScrollPane(); 191 192 if (jsc != null) { 193 int pos = jsc.getVerticalScrollBar().getModel().getValue(); 194 String nm = (lastTab != null) ? lastTab : ((tab != null) ? tab : node); 195 manager().storeScrollPosition(pos, nm); 196 } 197 } 198 } 199 200 205 public void setDescription(String title, String txt) { 206 this.description = txt; 207 this.title = title; 208 209 DescriptionComponent desc = findDescriptionComponent(); 210 211 if (desc != null) { 212 desc.setDescription(title, txt); 213 } 214 } 215 216 220 public void setHelpEnabled(boolean val) { 221 if (helpEnabled != val) { 222 helpEnabled = val; 223 224 DescriptionComponent desc = findDescriptionComponent(); 225 226 if (desc != null) { 227 desc.setHelpEnabled(val); 228 } 229 } 230 } 231 232 233 public void setMarginPainted(boolean val) { 234 if (marginPainted != val) { 235 marginPainted = val; 236 237 MarginViewportUI ui = findMVUI(); 238 239 if (ui != null) { 240 ui.setMarginPainted(val); 241 } 242 } 243 } 244 245 246 public void setMarginColor(Color c) { 247 if (!c.equals(marginColor)) { 248 marginColor = c; 249 250 MarginViewportUI ui = findMVUI(); 251 252 if (ui != null) { 253 ui.setMarginColor(c); 254 } 255 } 256 } 257 258 259 public void setEmptyString(String s) { 260 if (!s.equals(emptyString)) { 261 emptyString = s; 262 263 MarginViewportUI ui = findMVUI(); 264 265 if (ui != null) { 266 ui.setEmptyString(s); 267 } 268 } 269 } 270 271 274 private MarginViewportUI findMVUI() { 275 MarginViewportUI result = null; 276 JScrollPane pane = findScrollPane(); 277 278 if (pane != null) { 279 ViewportUI ui = pane.getViewport().getUI(); 280 281 if (ui instanceof MarginViewportUI) { 282 result = (MarginViewportUI) ui; 283 } else { 284 result = (MarginViewportUI) MarginViewportUI.createUI(pane.getViewport()); 286 pane.getViewport().setUI(result); 287 } 288 } 289 290 return result; 291 } 292 293 296 public void doLayout() { 297 Component [] c = getComponents(); 298 299 if (c.length > 0 && getWidth() >= 0 && getHeight() >= 0) { 300 Insets ins = getInsets(); 301 c[0].setBounds(ins.left, ins.top, getWidth() - (ins.right + ins.left), getHeight() - ins.top + ins.bottom); 302 303 if (c[0] instanceof JSplitPane && Boolean.TRUE.equals(firstSplit)) { 304 JSplitPane pane = (JSplitPane ) c[0]; 305 pane.setDividerLocation(0.80f); 306 pane.resetToPreferredSizes(); 307 308 JComponent dc = findDescriptionComponent(); 309 310 if (dc != null) { 311 if (dc.getHeight() > 0) { 312 firstSplit = Boolean.FALSE; 313 } 314 } else { 315 firstSplit = Boolean.FALSE; 316 } 317 } 318 319 if (c.length > 1) { 320 throw new IllegalStateException ("Hmm, something is wrong: " + Arrays.asList(c)); 321 } 322 } 323 } 324 325 326 public void requestFocus() { 327 JScrollPane jsc = findScrollPane(); 328 329 if ((jsc != null) && (jsc.getViewport().getView() != null)) { 330 jsc.getViewport().getView().requestFocus(); 331 } 332 } 333 334 335 public boolean requestFocusInWindow() { 336 JScrollPane jsc = findScrollPane(); 337 338 if ((jsc != null) && (jsc.getViewport().getView() != null)) { 339 return jsc.getViewport().getView().requestFocusInWindow(); 340 } else { 341 return false; 342 } 343 } 344 345 351 public void setState(int state) { 352 if (state != getState()) { 353 synchronized (getTreeLock()) { 354 switch (state) { 355 case 0: 356 357 JComponent tc = findTabbedContainer(); 358 359 if (tc != null) { 360 remove(tc); 361 } 362 363 JSplitPane jsp = findSplitPane(); 364 365 if (jsp != null) { 366 remove(jsp); 367 } 368 369 break; 370 371 case PSheet.STATE_HAS_DESCRIPTION: 372 373 JSplitPane split = findSplitPane(); 374 remove(findTabbedContainer()); 375 376 if (split != null) { 377 addImpl(split, null, 0); 378 } else { 379 addImpl(createDescriptionComponent(), null, 0); 380 } 381 382 break; 383 384 case PSheet.STATE_HAS_TABS: 385 386 JScrollPane jsc = findScrollPane(); 387 JComponent tct = findTabbedContainer(); 388 389 if (tct == null) { 390 addImpl(createTabbedContainer(), null, 0); 391 } 392 393 JSplitPane spl = findSplitPane(); 394 395 if (spl != null) { 396 remove(spl); 397 } 398 399 if (jsc != null) { 400 setTabbedContainerInnerComponent(findTabbedContainer(), jsc); 401 } 402 403 adjustForName(manager.getCurrentNodeName()); 404 405 break; 406 407 case (PSheet.STATE_HAS_DESCRIPTION | PSheet.STATE_HAS_TABS): 408 409 JComponent tcc = findTabbedContainer(); 410 JSplitPane splt = findSplitPane(); 411 JScrollPane scrl = findScrollPane(); 412 413 if (tcc == null) { 414 tcc = createTabbedContainer(); 415 addImpl(tcc, null, 0); 416 } 417 418 if (splt == null) { 419 addImpl(createDescriptionComponent(), null, 0); 420 splt = findSplitPane(); 421 } 422 423 setTabbedContainerInnerComponent(tcc, splt); 424 425 if (scrl != null) { 426 splt.setLeftComponent(scrl); 427 } 428 429 adjustForName(manager.getCurrentNodeName()); 430 431 break; 432 433 default: 434 throw new IllegalArgumentException (Integer.toString(state)); 435 } 436 } 437 } 438 439 revalidate(); 440 repaint(); 441 } 442 443 447 public int getState() { 448 int result = 0; 449 450 if (findTabbedContainer() != null) { 451 result |= STATE_HAS_TABS; 452 } 453 454 if (findSplitPane() != null) { 455 result |= STATE_HAS_DESCRIPTION; 456 } 457 458 return result; 459 } 460 461 483 protected void addImpl(Component comp, Object constraints, int idx) { 484 if ( 485 !(comp instanceof JSplitPane || comp instanceof JScrollPane || comp instanceof DescriptionComponent || 486 comp instanceof JTable || 487 (comp instanceof JComponent && Boolean.TRUE.equals(((JComponent ) comp).getClientProperty("tc")))) 488 ) { 489 throw new IllegalArgumentException ("Unexpected component " + comp); 490 } 491 492 synchronized (getTreeLock()) { 493 addCount++; 494 495 try { 496 if (!Arrays.asList(comp.getMouseListeners()).contains(this)) { 497 comp.addMouseListener(this); 498 } 499 500 if (comp instanceof JTable ) { 501 JScrollPane jsc = findScrollPane(); 502 503 if (jsc == null) { 504 jsc = createScrollPane(comp); 505 } else { 506 jsc.setViewportView(comp); 507 } 508 509 JSplitPane split = findSplitPane(); 510 511 if (split != null) { 512 split.setLeftComponent(jsc); 513 split.revalidate(); 514 } else { 515 JComponent tc = findTabbedContainer(); 516 517 if (tc != null) { 518 setTabbedContainerInnerComponent(tc, split); 519 } else { 520 addImpl(jsc, constraints, idx); 521 } 522 } 523 } else if (comp instanceof DescriptionComponent) { 524 JSplitPane pane = findSplitPane(); 525 boolean hadPane = pane != null; 526 527 if (pane == null) { 528 pane = createSplitPane(comp); 529 } 530 531 JScrollPane scroll = findScrollPane(); 532 533 if (scroll != null) { 534 pane.setLeftComponent(scroll); 535 } 536 537 if (!hadPane) { 538 addImpl(pane, constraints, idx); 539 } 540 541 ((DescriptionComponent) comp).setDescription(title, description); 542 ((DescriptionComponent) comp).setHelpEnabled(helpEnabled); 543 } else if (isTabbedContainer(comp)) { 544 JSplitPane split = findSplitPane(); 545 546 if (split != null) { 547 super.remove(split); 548 setTabbedContainerInnerComponent((JComponent ) comp, split); 549 } else { 550 JScrollPane pane = findScrollPane(); 551 552 if (pane != null) { 553 setTabbedContainerInnerComponent((JComponent ) comp, pane); 554 remove(pane); 555 } 556 } 557 558 super.addImpl(comp, constraints, idx); 559 } else if (comp instanceof JScrollPane ) { 560 JSplitPane split = findSplitPane(); 561 562 if (split != null) { 563 split.setLeftComponent(comp); 564 split.revalidate(); 565 } else { 566 JComponent tc = findTabbedContainer(); 567 568 if (tc != null) { 569 setTabbedContainerInnerComponent(tc, (JComponent ) comp); 570 } else { 571 super.addImpl(comp, constraints, idx); 572 } 573 } 574 } else if (comp instanceof JSplitPane ) { 575 JScrollPane jsc = findScrollPane(); 576 577 if (jsc != null) { 578 ((JSplitPane ) comp).setLeftComponent(jsc); 579 } 580 581 JComponent tc = findTabbedContainer(); 582 583 if (tc != null) { 584 setTabbedContainerInnerComponent(tc, (JComponent ) comp); 585 } else { 586 super.addImpl(comp, constraints, idx); 587 } 588 } else { 589 super.addImpl(comp, constraints, idx); 590 } 591 } finally { 592 addCount--; 593 revalidate(); 594 } 595 } 596 } 597 598 604 public void remove(Component c) { 605 if (c == null) { 606 return; 607 } 608 609 c.removeMouseListener(this); 610 611 synchronized (getTreeLock()) { 612 if (c.getParent() == this) { 613 super.remove(c); 614 615 if (adding()) { 616 return; 617 } 618 } 619 620 if (isTabbedContainer(c)) { 621 Component inner = getTabbedContainerInnerComponent((JComponent ) c); 622 623 if (inner != null) { 624 addImpl(inner, null, 0); 625 } 626 } else if (c instanceof JSplitPane ) { 627 if (c.getParent() != null) { 628 c.getParent().remove(c); 629 } 630 631 Component inner = ((JSplitPane ) c).getLeftComponent(); 632 633 if (inner != null) { 634 addImpl(inner, null, 0); 635 } 636 } else if (c instanceof DescriptionComponent) { 637 JSplitPane jsp = findSplitPane(); 638 639 if (jsp != null) { 640 jsp.remove(c); 641 remove(jsp); 642 } 643 } 644 } 645 646 revalidate(); 647 } 648 649 654 private boolean adding() { 655 return addCount > 0; 656 } 657 658 660 private DescriptionComponent createDescriptionComponent() { 661 return new DescriptionComponent(); 662 } 663 664 private JSplitPane createSplitPane(Component lower) { 665 JSplitPane pane = new JSplitPane (); 666 667 if (firstSplit == null) { 668 firstSplit = Boolean.TRUE; 669 } else { 670 firstSplit = Boolean.FALSE; 671 } 672 673 pane.setRightComponent(lower); 674 pane.setOrientation(JSplitPane.VERTICAL_SPLIT); 675 pane.setContinuousLayout(true); 676 pane.setResizeWeight(1); 677 pane.setDividerLocation(0.80f); 678 pane.setBorder(BorderFactory.createEmptyBorder()); 679 pane.setUI(PropUtils.createSplitPaneUI()); 680 681 pane.getActionMap().getParent().remove("toggleFocus"); 684 685 return pane; 686 } 687 688 private JScrollPane createScrollPane(Component inner) { 689 JScrollPane result = new JScrollPane (inner); 690 JViewport vp = result.getViewport(); 691 vp.addMouseListener(this); 692 693 MarginViewportUI ui = (MarginViewportUI) MarginViewportUI.createUI(vp); 694 vp.setUI(ui); 695 ui.setMarginPainted(marginPainted); 696 ui.setMarginColor(marginColor); 697 ui.setEmptyString(emptyString); 698 result.setBorder(BorderFactory.createEmptyBorder()); 699 result.setViewportBorder(result.getBorder()); 700 701 return result; 702 } 703 704 private JComponent createTabbedContainer() { 705 JComponent result = TabbedContainerBridge.getDefault().createTabbedContainer(); 706 result.putClientProperty("tc", Boolean.TRUE); 707 configureTabbedContainer(tabbedContainerObjects, tabbedContainerTitles, result); 708 709 if (selectionListener != null) { 710 TabbedContainerBridge.getDefault().attachSelectionListener(result, selectionListener); 711 } 712 713 return result; 714 } 715 716 public void setTabbedContainerItems(Object [] o, String [] s) { 717 adjusting = true; 718 719 try { 720 tabbedContainerObjects = o; 721 tabbedContainerTitles = s; 722 723 if (o.length == 0) { 724 int newState = ((getState() & STATE_HAS_DESCRIPTION) != 0) ? STATE_HAS_DESCRIPTION : 0; 725 setState(newState); 726 } else { 727 configureTabbedContainer(o, s, null); 728 } 729 } finally { 730 adjusting = false; 731 } 732 } 733 734 public void setTabbedContainerSelection(Object item) { 735 JComponent tabbed = findTabbedContainer(); 736 737 if (tabbed != null) { 738 adjusting = true; 739 740 try { 741 TabbedContainerBridge.getDefault().setSelectedItem(tabbed, item); 742 } finally { 743 adjusting = false; 744 } 745 } 746 } 747 748 public Object getTabbedContainerSelection() { 749 JComponent tabbed = findTabbedContainer(); 750 751 if (tabbed != null) { 752 Object o = TabbedContainerBridge.getDefault().getSelectedItem(tabbed); 753 754 if (o instanceof Node.PropertySet[]) { 756 return o; 757 } 758 } 759 760 return null; 761 } 762 763 private void configureTabbedContainer(Object [] o, String [] s, JComponent cont) { 764 if (cont == null) { 765 cont = findTabbedContainer(); 766 } 767 768 if (cont != null) { 769 TabbedContainerBridge.getDefault().setItems(cont, o, s); 770 } 771 } 772 773 private void setTabbedContainerInnerComponent(JComponent tabbed, JComponent comp) { 774 if (tabbed == null) { 775 tabbed = findTabbedContainer(); 776 } 777 778 TabbedContainerBridge.getDefault().setInnerComponent(tabbed, comp); 779 } 780 781 private static JComponent getTabbedContainerInnerComponent(JComponent c) { 782 JComponent result = TabbedContainerBridge.getDefault().getInnerComponent(c); 783 784 return result; 785 } 786 787 public void addSelectionChangeListener(ChangeListener l) { 788 if (selectionListener != l) { 789 JComponent comp = findTabbedContainer(); 790 selectionListener = l; 791 792 if (comp != null) { 793 TabbedContainerBridge.getDefault().attachSelectionListener(comp, l); 794 } 795 } 796 } 797 798 private static boolean isTabbedContainer(Component comp) { 799 return comp instanceof JComponent && Boolean.TRUE.equals(((JComponent ) comp).getClientProperty("tc")); } 801 802 805 private DescriptionComponent findDescriptionComponent() { 806 return (DescriptionComponent) findChildOfClass(findSplitPane(), DescriptionComponent.class); 807 } 808 809 813 private JScrollPane findScrollPane() { 814 JScrollPane result = (JScrollPane ) findChildOfClass(this, JScrollPane .class); 815 816 if (result == null) { 817 result = (JScrollPane ) findChildOfClass(findTabbedContainer(), JScrollPane .class); 818 819 if (result == null) { 820 result = (JScrollPane ) findChildOfClass(findSplitPane(), JScrollPane .class); 821 } 822 } 823 824 return result; 825 } 826 827 830 private JSplitPane findSplitPane() { 831 JSplitPane result = (JSplitPane ) findChildOfClass(this, JSplitPane .class); 832 833 if (result == null) { 834 result = (JSplitPane ) findChildOfClass(findTabbedContainer(), JSplitPane .class); 835 } 836 837 return result; 838 } 839 840 843 private JComponent findTabbedContainer() { 844 Component [] c = getComponents(); 845 846 for (int i = 0; i < c.length; i++) { 847 if (c[i] instanceof JComponent && Boolean.TRUE.equals(((JComponent ) c[i]).getClientProperty("tc"))) { 848 return (JComponent ) c[i]; 849 } 850 } 851 852 return null; 853 } 854 855 858 private static Component findChildOfClass(Container container, Class clazz) { 859 if (container == null) { 860 return null; 861 } 862 863 if (isTabbedContainer((JComponent ) container)) { 864 Component c = getTabbedContainerInnerComponent((JComponent ) container); 865 866 if ((c != null) && (c.getClass() == clazz)) { 867 return c; 868 } 869 } else { 870 Component [] c = container.getComponents(); 871 872 for (int i = 0; i < c.length; i++) { 873 if (clazz == c[i].getClass()) { 874 return c[i]; 875 } 876 } 877 } 878 879 return null; 880 } 881 882 885 protected void popupRequested(Point p) { 886 PropertySheet ps = (PropertySheet) SwingUtilities.getAncestorOfClass(PropertySheet.class, this); 887 888 if (ps != null) { 889 ps.showPopup(p); 890 } 891 } 892 893 896 protected void helpRequested() { 897 PropertySheet ps = (PropertySheet) SwingUtilities.getAncestorOfClass(PropertySheet.class, this); 898 899 if (ps != null) { 900 ps.helpAction.actionPerformed(new ActionEvent (this, ActionEvent.ACTION_PERFORMED, "invokeHelp")); } 902 } 903 904 public void mousePressed(MouseEvent e) { 905 if (e.isPopupTrigger()) { 906 Point p = SwingUtilities.convertPoint((Component ) e.getSource(), e.getPoint(), this); 907 updateSheetTableSelection(e); 908 popupRequested(p); 909 } 910 } 911 912 public void mouseReleased(MouseEvent e) { 913 if (e.isPopupTrigger()) { 914 Point p = SwingUtilities.convertPoint((Component ) e.getSource(), e.getPoint(), this); 915 updateSheetTableSelection(e); 916 popupRequested(p); 917 } 918 } 919 920 private void updateSheetTableSelection(MouseEvent e) { 921 Component comp = (Component ) e.getSource(); 922 923 if (comp instanceof SheetTable) { 924 SheetTable table = (SheetTable) comp; 925 table.changeSelection(table.rowAtPoint(e.getPoint()), 0, false, false); 926 } 927 } 928 929 public void mouseClicked(MouseEvent e) { 930 } 932 933 public void mouseEntered(MouseEvent e) { 934 } 936 937 public void mouseExited(MouseEvent e) { 938 } 940 941 private class PopupAction extends AbstractAction { 942 public void actionPerformed(ActionEvent actionEvent) { 943 popupRequested(new Point (0, 0)); 944 } 945 } 946 947 private class SwitchTabAction extends AbstractAction { 948 private int increment; 949 public SwitchTabAction( int increment ) { 950 this.increment = increment; 951 } 952 953 public void actionPerformed(ActionEvent actionEvent) { 954 JComponent tabbed = findTabbedContainer(); 955 956 if( null == tabbed ) 957 return; 958 959 Object o = TabbedContainerBridge.getDefault().getSelectedItem( tabbed ); 960 if( null == o ) 961 return; 962 963 Object [] items = TabbedContainerBridge.getDefault().getItems( tabbed ); 964 int currentIndex = -1; 965 for( int i=0; null != items && i<items.length; i++ ) { 966 if( items[i].equals( o ) ) { 967 currentIndex = i; 968 break; 969 } 970 } 971 972 if( currentIndex < 0 ) 973 return; 974 975 int newIndex = currentIndex + increment; 976 if( newIndex < 0 ) 977 newIndex = items.length-1; 978 if( newIndex >= items.length ) 979 newIndex = 0; 980 TabbedContainerBridge.getDefault().setSelectedItem( tabbed, items[newIndex] ); 981 } 982 } 983 } 984 | Popular Tags |