1 11 package org.eclipse.debug.internal.ui.viewers; 12 13 import java.util.HashMap ; 14 import java.util.Iterator ; 15 import java.util.List ; 16 import java.util.Map ; 17 18 import org.eclipse.core.runtime.Assert; 19 import org.eclipse.core.runtime.IAdaptable; 20 import org.eclipse.core.runtime.IProgressMonitor; 21 import org.eclipse.core.runtime.IStatus; 22 import org.eclipse.core.runtime.Platform; 23 import org.eclipse.core.runtime.Status; 24 import org.eclipse.debug.internal.ui.DebugUIPlugin; 25 import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelChangedListener; 26 import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelProxy; 27 import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelSelectionPolicy; 28 import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelSelectionPolicyFactory; 29 import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext; 30 import org.eclipse.debug.internal.ui.viewers.model.provisional.IStatusMonitor; 31 import org.eclipse.debug.internal.ui.viewers.model.provisional.PresentationContext; 32 import org.eclipse.jface.resource.ImageDescriptor; 33 import org.eclipse.jface.viewers.ISelection; 34 import org.eclipse.jface.viewers.IStructuredContentProvider; 35 import org.eclipse.jface.viewers.IStructuredSelection; 36 import org.eclipse.jface.viewers.SelectionChangedEvent; 37 import org.eclipse.jface.viewers.StructuredSelection; 38 import org.eclipse.jface.viewers.StructuredViewer; 39 import org.eclipse.jface.viewers.Viewer; 40 import org.eclipse.swt.SWT; 41 import org.eclipse.swt.events.SelectionEvent; 42 import org.eclipse.swt.graphics.Color; 43 import org.eclipse.swt.graphics.Font; 44 import org.eclipse.swt.graphics.FontData; 45 import org.eclipse.swt.graphics.Image; 46 import org.eclipse.swt.graphics.RGB; 47 import org.eclipse.swt.widgets.Control; 48 import org.eclipse.swt.widgets.Event; 49 import org.eclipse.swt.widgets.Item; 50 import org.eclipse.swt.widgets.Listener; 51 import org.eclipse.swt.widgets.Widget; 52 import org.eclipse.ui.progress.WorkbenchJob; 53 54 74 public abstract class AsynchronousViewer extends StructuredViewer implements Listener { 75 76 79 private AsynchronousModel fModel; 80 81 86 private Map fImageCache = new HashMap (); 87 88 93 private Map fFontCache = new HashMap (); 94 95 100 private Map fColorCache = new HashMap (); 101 102 106 private IPresentationContext fContext; 107 108 private ISelection fPendingSelection; 109 110 private ISelection fCurrentSelection; 111 112 120 private int[] fSetDataIndicies = new int[5]; 121 122 125 private AbstractUpdatePolicy fUpdatePolicy; 126 127 protected static final String OLD_LABEL = "old_label"; protected static final String OLD_IMAGE = "old_image"; 130 public static boolean DEBUG_VIEWER = false; 132 133 static { 134 DEBUG_VIEWER = DebugUIPlugin.DEBUG && "true".equals( Platform.getDebugOption("org.eclipse.debug.ui/debug/viewers/viewer")); } 137 138 141 protected AsynchronousViewer() { 142 setContentProvider(new NullContentProvider()); 143 setUseHashlookup(true); 144 } 145 146 149 public final void setUseHashlookup(boolean enable) { 150 Assert.isTrue(enable); 151 super.setUseHashlookup(enable); 152 } 153 154 157 protected void hookControl(Control control) { 158 super.hookControl(control); 159 control.addListener(SWT.SetData, this); 160 } 161 162 166 public synchronized void dispose() { 167 Iterator images = fImageCache.values().iterator(); 168 while (images.hasNext()) { 169 Image image = (Image) images.next(); 170 image.dispose(); 171 } 172 fImageCache.clear(); 173 174 Iterator fonts = fFontCache.values().iterator(); 175 while (fonts.hasNext()) { 176 Font font = (Font) fonts.next(); 177 font.dispose(); 178 } 179 fFontCache.clear(); 180 181 Iterator colors = fColorCache.values().iterator(); 182 while (colors.hasNext()) { 183 Color color = (Color) colors.next(); 184 color.dispose(); 185 } 186 fColorCache.clear(); 187 188 if (fModel != null) { 189 fModel.dispose(); 190 } 191 if (fUpdatePolicy != null) { 192 fUpdatePolicy.dispose(); 193 } 194 if (fContext != null) { 195 ((PresentationContext)fContext).dispose(); 196 } 197 } 198 199 204 public void update(Object element) { 205 ModelNode[] nodes = getModel().getNodes(element); 206 if (nodes != null) { 207 for (int i = 0; i < nodes.length; i++) { 208 updateLabel(nodes[i]); 209 } 210 } 211 } 212 213 219 protected void updateLabel(ModelNode node) { 220 if (!node.getElement().equals(getInput())) { 222 getModel().updateLabel(node); 223 } 224 } 225 226 233 public IPresentationContext getPresentationContext() { 234 return fContext; 235 } 236 237 240 protected synchronized void unmapAllElements() { 241 super.unmapAllElements(); 242 AsynchronousModel model = getModel(); 243 if (model != null) { 244 model.dispose(); 245 } 246 } 247 248 251 protected synchronized void inputChanged(Object input, Object oldInput) { 252 fPendingSelection = null; 253 if (fCurrentSelection != null) { 254 updateSelection(new StructuredSelection()); 255 fCurrentSelection = null; 256 } 257 if (fUpdatePolicy == null) { 258 fUpdatePolicy = createUpdatePolicy(); 259 fUpdatePolicy.init(this); 260 } 261 if (fModel != null) { 262 fModel.dispose(); 263 } 264 fModel = createModel(); 265 fModel.init(input); 266 if (input != null) { 267 mapElement(fModel.getRootNode(), getControl()); 268 getControl().setData(fModel.getRootNode().getElement()); 269 } else { 270 unmapAllElements(); 271 getControl().setData(null); 272 } 273 refresh(); 274 } 275 276 282 protected abstract AsynchronousModel createModel(); 283 284 288 public abstract AbstractUpdatePolicy createUpdatePolicy(); 289 290 Image[] getImages(ImageDescriptor[] descriptors) { 291 if (descriptors == null || descriptors.length == 0) { 292 String [] columns = getPresentationContext().getColumns(); 293 if (columns == null) { 294 return new Image[1]; 295 } else { 296 return new Image[columns.length]; 297 } 298 } 299 Image[] images = new Image[descriptors.length]; 300 for (int i = 0; i < images.length; i++) { 301 images[i] = getImage(descriptors[i]); 302 } 303 return images; 304 } 305 306 314 protected Image getImage(ImageDescriptor descriptor) { 315 if (descriptor == null) { 316 return null; 317 } 318 Image image = (Image) fImageCache.get(descriptor); 319 if (image == null) { 320 image = new Image(getControl().getDisplay(), descriptor.getImageData()); 321 fImageCache.put(descriptor, image); 322 } 323 return image; 324 } 325 326 protected Font[] getFonts(FontData[] fontDatas) { 327 if (fontDatas == null || fontDatas.length == 0) { 328 String [] columns = getPresentationContext().getColumns(); 329 if (columns == null) { 330 return new Font[1]; 331 } else { 332 return new Font[columns.length]; 333 } 334 } 335 336 Font[] fonts = new Font[fontDatas.length]; 337 for (int i = 0; i < fonts.length; i++) { 338 fonts[i] = getFont(fontDatas[i]); 339 } 340 return fonts; 341 } 342 343 350 protected Font getFont(FontData fontData) { 351 if (fontData == null) { 352 return null; 353 } 354 Font font = (Font) fFontCache.get(fontData); 355 if (font == null) { 356 font = new Font(getControl().getDisplay(), fontData); 357 fFontCache.put(fontData, font); 358 } 359 return font; 360 } 361 362 protected Color[] getColors(RGB[] rgb) { 363 if (rgb == null || rgb.length == 0) { 364 String [] columns = getPresentationContext().getColumns(); 365 if (columns == null) { 366 return new Color[1]; 367 } else { 368 return new Color[columns.length]; 369 } 370 } 371 Color[] colors = new Color[rgb.length]; 372 for (int i = 0; i < colors.length; i++) { 373 colors[i] = getColor(rgb[i]); 374 } 375 return colors; 376 } 377 384 protected Color getColor(RGB rgb) { 385 if (rgb == null) { 386 return null; 387 } 388 Color color = (Color) fColorCache.get(rgb); 389 if (color == null) { 390 color = new Color(getControl().getDisplay(), rgb); 391 fColorCache.put(rgb, color); 392 } 393 return color; 394 } 395 396 401 public void setContext(IPresentationContext context) { 402 fContext = context; 403 } 404 405 408 protected Widget doFindItem(Object element) { 409 AsynchronousModel model = getModel(); 411 if (model != null) { 412 if (element.equals(model.getRootNode())) { 413 return doFindInputItem(element); 414 } 415 Widget[] widgets = findItems(element); 416 if (widgets.length > 0) { 417 return widgets[0]; 418 } 419 } 420 return null; 421 } 422 423 428 protected Widget doFindInputItem(Object element) { 429 if (element instanceof ModelNode) { 430 ModelNode node = (ModelNode) element; 431 if (node.getElement().equals(getInput())) { 432 return getControl(); 433 } 434 } 435 return null; 436 } 437 438 441 protected void doUpdateItem(Widget item, Object element, boolean fullMap) { 442 } 443 444 447 protected void internalRefresh(Object element) { 448 AsynchronousModel model = getModel(); 450 if (model != null) { 451 ModelNode[] nodes = model.getNodes(element); 452 if (nodes != null) { 453 for (int i = 0; i < nodes.length; i++) { 454 ModelNode node = nodes[i]; 455 Widget item = findItem(node); 457 if (item != null) { 458 internalRefresh(node); 459 } 460 } 461 } 462 } 463 } 464 465 472 protected void internalRefresh(ModelNode node) { 473 updateLabel(node); 474 } 475 476 479 public synchronized void setSelection(ISelection selection, boolean reveal) { 480 setSelection(selection, reveal, false); 481 } 482 483 491 public synchronized void setSelection(ISelection selection, final boolean reveal, boolean force) { 492 Control control = getControl(); 493 if (control == null || control.isDisposed()) { 494 return; 495 } 496 if (!acceptsSelection(selection)) { 497 selection = getEmptySelection(); 498 } 499 if (!force && !overrideSelection(fCurrentSelection, selection)) { 500 return; 501 } 502 503 fPendingSelection = selection; 504 505 if (getControl().getDisplay().getThread() == Thread.currentThread()) { 506 attemptSelection(reveal); 507 } else { 508 WorkbenchJob job = new WorkbenchJob("attemptSelection") { public IStatus runInUIThread(IProgressMonitor monitor) { 510 attemptSelection(reveal); 511 return Status.OK_STATUS; 512 } 513 514 }; 515 job.setSystem(true); 516 job.schedule(); 517 } 518 } 519 520 521 529 protected boolean overrideSelection(ISelection current, ISelection candidate) { 530 IModelSelectionPolicy selectionPolicy = getSelectionPolicy(current); 531 if (selectionPolicy == null) { 532 return true; 533 } 534 if (selectionPolicy.contains(candidate, getPresentationContext())) { 535 return selectionPolicy.overrides(current, candidate, getPresentationContext()); 536 } 537 return !selectionPolicy.isSticky(current, getPresentationContext()); 538 } 539 540 543 public ISelection getSelection() { 544 Control control = getControl(); 545 if (control == null || control.isDisposed() || fCurrentSelection == null) { 546 return StructuredSelection.EMPTY; 547 } 548 return fCurrentSelection; 549 } 550 551 554 protected void handleSelect(SelectionEvent event) { 555 Control control = getControl(); 557 if (control != null && !control.isDisposed()) { 558 updateSelection(newSelectionFromWidget()); 559 } 560 } 561 562 565 protected void handlePostSelect(SelectionEvent e) { 566 SelectionChangedEvent event = new SelectionChangedEvent(this, newSelectionFromWidget()); 567 firePostSelectionChanged(event); 568 } 569 570 576 protected abstract ISelection newSelectionFromWidget(); 577 578 585 protected IModelSelectionPolicy getSelectionPolicy(ISelection selection) { 586 if (selection instanceof IStructuredSelection) { 587 IStructuredSelection ss = (IStructuredSelection) selection; 588 Object element = ss.getFirstElement(); 589 if (element instanceof IAdaptable) { 590 IAdaptable adaptable = (IAdaptable) element; 591 IModelSelectionPolicyFactory factory = (IModelSelectionPolicyFactory) adaptable.getAdapter(IModelSelectionPolicyFactory.class); 592 if (factory != null) { 593 return factory.createModelSelectionPolicyAdapter(adaptable, getPresentationContext()); 594 } 595 } 596 } 597 return null; 598 } 599 600 603 final protected void setSelectionToWidget(ISelection selection, final boolean reveal) { 604 throw new IllegalArgumentException ("This method should not be called"); } 607 608 611 final protected void setSelectionToWidget(List l, boolean reveal) { 612 throw new IllegalArgumentException ("This method should not be called"); } 615 616 621 protected void attemptSelection(boolean reveal) { 622 ISelection currentSelection = null; 623 synchronized (this) { 624 if (fPendingSelection != null) { 625 ISelection remaining = doAttemptSelectionToWidget(fPendingSelection, reveal); 626 if (remaining.isEmpty()) { 627 remaining = null; 628 } 629 if (!fPendingSelection.equals(remaining)) { 630 fPendingSelection = remaining; 631 currentSelection = newSelectionFromWidget(); 632 if (isSuppressEqualSelections() && currentSelection.equals(fCurrentSelection)) { 633 return; 634 } 635 } 636 } 637 } 638 if (currentSelection != null) { 639 updateSelection(currentSelection); 640 firePostSelectionChanged(new SelectionChangedEvent(this, currentSelection)); 641 } 642 } 643 644 654 protected boolean isSuppressEqualSelections() { 655 return true; 656 } 657 658 667 protected abstract ISelection doAttemptSelectionToWidget(ISelection selection, boolean reveal); 668 669 675 protected abstract boolean acceptsSelection(ISelection selection); 676 677 682 protected abstract ISelection getEmptySelection(); 683 684 687 private class NullContentProvider implements IStructuredContentProvider { 688 public void dispose() { 689 } 690 691 public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { 692 } 693 694 public Object [] getElements(Object inputElement) { 695 return null; 696 } 697 } 698 699 707 protected void handlePresentationFailure(IStatusMonitor monitor, IStatus status) { 708 } 709 710 713 protected synchronized void preservingSelection(Runnable updateCode) { 714 if (fPendingSelection == null || fPendingSelection.isEmpty()) { 715 ISelection oldSelection = null; 716 try { 717 oldSelection = fCurrentSelection; 719 updateCode.run(); 721 } finally { 722 if (oldSelection == null) { 724 oldSelection = new StructuredSelection(); 725 } 726 if (getControl().getDisplay().getThread() == Thread.currentThread()) { 727 if (!oldSelection.equals(newSelectionFromWidget())) { 728 restoreSelection(oldSelection); 729 } 730 } else { 731 WorkbenchJob job = new WorkbenchJob("attemptSelection") { public IStatus runInUIThread(IProgressMonitor monitor) { 733 synchronized (AsynchronousViewer.this) { 734 if (!getControl().isDisposed()) { 735 if (fPendingSelection == null || fPendingSelection.isEmpty()) { 736 ISelection tempSelection = fCurrentSelection; 737 if (tempSelection == null) { 738 tempSelection = new StructuredSelection(); 739 } 740 if (!tempSelection.equals(newSelectionFromWidget())) { 741 restoreSelection(tempSelection); 742 } 743 } 744 } 745 } 746 return Status.OK_STATUS; 747 } 748 749 }; 750 job.setSystem(true); 751 job.schedule(); 752 } 753 } 754 } else { 755 updateCode.run(); 756 } 757 } 758 759 protected synchronized void restoreSelection(ISelection oldSelection) { 760 ISelection remaining = doAttemptSelectionToWidget(oldSelection, false); 761 fCurrentSelection = newSelectionFromWidget(); 763 if (!selectionExists(fCurrentSelection)) { 764 if (selectionExists(oldSelection)) { 765 fCurrentSelection = oldSelection; 767 } else { 768 fCurrentSelection = getEmptySelection(); 769 } 770 } 771 if (!fCurrentSelection.equals(oldSelection)) { 772 handleInvalidSelection(oldSelection, fCurrentSelection); 773 if (selectionExists(remaining)) { 775 setSelection(remaining); 776 } 777 } 778 } 779 780 783 protected boolean selectionExists(ISelection selection) { 784 if (selection.isEmpty()) { 785 return false; 786 } 787 if (selection instanceof IStructuredSelection) { 788 IStructuredSelection ss = (IStructuredSelection) selection; 789 Iterator iterator = ss.iterator(); 790 while (iterator.hasNext()) { 791 Object element = iterator.next(); 792 if (getModel().getNodes(element) == null) { 793 return false; 794 } 795 } 796 } 797 return true; 798 } 799 800 807 protected abstract void setColors(Widget widget, RGB foreground[], RGB background[]); 808 809 816 protected abstract void setLabels(Widget widget, String [] text, ImageDescriptor[] image); 817 818 824 protected abstract void setFonts(Widget widget, FontData[] font); 825 826 829 protected synchronized void updateSelection(ISelection selection) { 830 fCurrentSelection = selection; 831 super.updateSelection(selection); 832 } 833 834 835 836 841 protected void modelProxyAdded(IModelProxy proxy) { 842 if (fUpdatePolicy instanceof IModelChangedListener) { 843 proxy.addModelChangedListener((IModelChangedListener)fUpdatePolicy); 844 } 845 } 846 847 852 protected void modelProxyRemoved(IModelProxy proxy) { 853 if (fUpdatePolicy instanceof IModelChangedListener) { 854 proxy.removeModelChangedListener((IModelChangedListener)fUpdatePolicy); 855 } 856 } 857 858 863 protected AsynchronousModel getModel() { 864 return fModel; 865 } 866 867 872 protected void nodeChanged(ModelNode node) { 873 Widget widget = findItem(node); 874 if (widget != null) { 875 clear(widget); 876 attemptPendingUpdates(); 877 } 878 } 879 880 883 public synchronized boolean hasPendingUpdates() { 884 return getModel().hasPendingUpdates(); 885 } 886 887 893 protected void updateComplete(IStatusMonitor monitor) { 894 } 895 896 901 protected abstract void clear(Widget item); 902 903 908 protected abstract void clearChildren(Widget item); 909 910 916 protected abstract void clearChild(Widget parent, int childIndex); 917 918 926 protected abstract Widget getChildWidget(Widget parent, int index); 927 928 934 protected abstract void setItemCount(Widget parent, int itemCount); 935 936 939 protected void attemptPendingUpdates() { 940 attemptSelection(false); 941 } 942 943 950 protected void nodeChildrenChanged(ModelNode parentNode) { 951 Widget widget = findItem(parentNode); 952 if (widget != null && !widget.isDisposed()) { 953 int childCount = parentNode.getChildCount(); 954 setItemCount(widget, childCount); 955 clearChildren(widget); 956 attemptPendingUpdates(); 957 } 958 } 959 960 966 protected void nodeChildrenAdded(ModelNode parentNode) { 967 Widget widget = findItem(parentNode); 968 if (widget != null && !widget.isDisposed()) { 969 int childCount = parentNode.getChildCount(); 970 setItemCount(widget, childCount); 971 attemptPendingUpdates(); 972 } 973 } 974 975 981 protected void nodeChildRemoved(ModelNode parentNode, int index) { 982 Widget widget = findItem(parentNode); 983 if (widget != null && !widget.isDisposed()) { 984 Widget childWidget = getChildWidget(widget, index); 985 int childCount = parentNode.getChildCount(); 986 if (childWidget == null) { 988 setItemCount(widget, childCount); 989 } else { 990 childWidget.dispose(); 991 } 992 for (int i = index; i < childCount; i ++) { 993 clearChild(widget, i); 994 } 995 attemptPendingUpdates(); 996 } 997 } 998 999 1005 protected void unmapNode(ModelNode node) { 1006 unmapElement(node); 1007 ModelNode[] childrenNodes = node.getChildrenNodes(); 1008 if (childrenNodes != null) { 1009 for (int i = 0; i < childrenNodes.length; i++) { 1010 unmapNode(childrenNodes[i]); 1011 } 1012 } 1013 } 1014 1015 1020 protected ModelNode findNode(Widget widget) { 1021 ModelNode[] nodes = getModel().getNodes(widget.getData()); 1022 if (nodes != null) { 1023 for (int i = 0; i < nodes.length; i++) { 1024 ModelNode node = nodes[i]; 1025 Widget item = findItem(node); 1026 if (widget == item) { 1027 return node; 1028 } 1029 } 1030 } 1031 return null; 1032 } 1033 1034 1039 protected Widget findItem(ModelNode node) { 1040 return findItem((Object )node); 1041 } 1042 1043 1050 public void handleEvent(final Event event) { 1051 update((Item)event.item, event.index); 1052 } 1053 1054 1060 protected void update(Item item, int index) { 1061 restoreLabels(item); 1062 int level = 0; 1063 1064 Widget parentItem = getParentWidget(item); 1065 if (DEBUG_VIEWER) { 1066 DebugUIPlugin.debug("SET DATA [" + index + "]: " + parentItem); } 1068 ModelNode node = null; 1069 if (parentItem != null) { 1072 ModelNode[] nodes = getModel().getNodes(parentItem.getData()); 1073 if (nodes != null) { 1074 for (int i = 0; i < nodes.length; i++) { 1075 ModelNode parentNode = nodes[i]; 1076 Widget parentWidget = findItem(parentNode); 1077 if (parentWidget == parentItem) { 1078 ModelNode[] childrenNodes = parentNode.getChildrenNodes(); 1079 if (childrenNodes != null && index < childrenNodes.length) { 1080 node = childrenNodes[index]; 1081 } 1082 } 1083 } 1084 } 1085 } 1086 1087 if (node == null) { 1089 setNodeIndex(index, level); 1090 while (parentItem instanceof Item) { 1091 level++; 1092 Widget parent = getParentWidget(parentItem); 1093 int pindex = indexOf(parent, parentItem); 1094 if (pindex < 0) { 1095 return; 1096 } 1097 setNodeIndex(pindex, level); 1098 parentItem = parent; 1099 } 1100 1101 node = getModel().getRootNode(); 1102 if (node == null) { 1103 if (DEBUG_VIEWER) { 1104 DebugUIPlugin.debug("\tFAILED - root model node is null"); } 1106 return; 1107 } 1108 for (int i = level; i >= 0; i--) { 1109 ModelNode[] childrenNodes = node.getChildrenNodes(); 1110 if (childrenNodes == null) { 1111 if (DEBUG_VIEWER) { 1112 DebugUIPlugin.debug("\tFAILED - no children nodes for " + node); } 1114 return; 1115 } 1116 int pindex = getNodeIndex(i); 1117 if (pindex < childrenNodes.length) { 1118 node = childrenNodes[pindex]; 1119 } else { 1120 if (DEBUG_VIEWER) { 1121 DebugUIPlugin.debug("\tFAILED - no children nodes for " + node); } 1123 return; 1124 } 1125 } 1126 } 1127 1128 1129 if (node != null) { 1131 mapElement(node, item); 1132 item.setData(node.getElement()); 1133 if (DEBUG_VIEWER) { 1134 DebugUIPlugin.debug("\titem mapped: " + node); } 1136 internalRefresh(node); 1137 } else { 1138 if (DEBUG_VIEWER) { 1139 DebugUIPlugin.debug("\tFAILED - unable to find corresponding node"); } 1141 } 1142 } 1143 1144 1151 private void setNodeIndex(int nodeIndex, int level) { 1152 if (level > (fSetDataIndicies.length - 1)) { 1153 int[] next = new int[level+5]; 1155 System.arraycopy(fSetDataIndicies, 0, next, 0, fSetDataIndicies.length); 1156 fSetDataIndicies = next; 1157 } 1158 fSetDataIndicies[level] = nodeIndex; 1159 } 1160 1161 1168 private int getNodeIndex(int level) { 1169 return fSetDataIndicies[level]; 1170 } 1171 1172 protected abstract int indexOf(Widget parent, Widget child); 1173 1174 protected abstract void restoreLabels(Item item); 1175 1176 1182 protected abstract Widget getParentWidget(Widget widget); 1183 1184 1190 protected void updateChildren(ModelNode parent) { 1191 getModel().updateChildren(parent); 1192 } 1193} 1194 | Popular Tags |