]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.scenegraph.ui/src/org/simantics/scenegraph/ui/SceneGraphViewPart.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.scenegraph.ui / src / org / simantics / scenegraph / ui / SceneGraphViewPart.java
1 /*******************************************************************************\r
2  * Copyright (c) 2007, 2010 Association for Decentralized Information Management\r
3  * in Industry THTH ry.\r
4  * All rights reserved. This program and the accompanying materials\r
5  * are made available under the terms of the Eclipse Public License v1.0\r
6  * which accompanies this distribution, and is available at\r
7  * http://www.eclipse.org/legal/epl-v10.html\r
8  *\r
9  * Contributors:\r
10  *     VTT Technical Research Centre of Finland - initial API and implementation\r
11  *******************************************************************************/\r
12 package org.simantics.scenegraph.ui;\r
13 \r
14 import java.io.ByteArrayOutputStream;\r
15 import java.io.PrintStream;\r
16 import java.lang.ref.WeakReference;\r
17 import java.util.List;\r
18 \r
19 import org.eclipse.core.commands.Command;\r
20 import org.eclipse.core.commands.State;\r
21 import org.eclipse.jface.layout.TreeColumnLayout;\r
22 import org.eclipse.jface.resource.ImageDescriptor;\r
23 import org.eclipse.jface.resource.JFaceResources;\r
24 import org.eclipse.jface.resource.LocalResourceManager;\r
25 import org.eclipse.jface.viewers.ColumnLabelProvider;\r
26 import org.eclipse.jface.viewers.ColumnWeightData;\r
27 import org.eclipse.jface.viewers.DoubleClickEvent;\r
28 import org.eclipse.jface.viewers.IDoubleClickListener;\r
29 import org.eclipse.jface.viewers.ISelectionChangedListener;\r
30 import org.eclipse.jface.viewers.IStructuredSelection;\r
31 import org.eclipse.jface.viewers.ITreeContentProvider;\r
32 import org.eclipse.jface.viewers.SelectionChangedEvent;\r
33 import org.eclipse.jface.viewers.TreePath;\r
34 import org.eclipse.jface.viewers.TreeViewer;\r
35 import org.eclipse.jface.viewers.TreeViewerColumn;\r
36 import org.eclipse.jface.viewers.Viewer;\r
37 import org.eclipse.jface.viewers.ViewerCell;\r
38 import org.eclipse.swt.SWT;\r
39 import org.eclipse.swt.dnd.Clipboard;\r
40 import org.eclipse.swt.dnd.TextTransfer;\r
41 import org.eclipse.swt.dnd.Transfer;\r
42 import org.eclipse.swt.graphics.Image;\r
43 import org.eclipse.swt.widgets.Composite;\r
44 import org.eclipse.swt.widgets.Shell;\r
45 import org.eclipse.ui.IPartListener2;\r
46 import org.eclipse.ui.IPartService;\r
47 import org.eclipse.ui.IWorkbenchPage;\r
48 import org.eclipse.ui.IWorkbenchPart;\r
49 import org.eclipse.ui.IWorkbenchPartReference;\r
50 import org.eclipse.ui.IWorkbenchWindow;\r
51 import org.eclipse.ui.PlatformUI;\r
52 import org.eclipse.ui.commands.ICommandService;\r
53 import org.eclipse.ui.contexts.IContextActivation;\r
54 import org.eclipse.ui.contexts.IContextService;\r
55 import org.eclipse.ui.part.ViewPart;\r
56 import org.simantics.scenegraph.ILookupService;\r
57 import org.simantics.scenegraph.INode;\r
58 import org.simantics.scenegraph.g2d.G2DParentNode;\r
59 import org.simantics.scenegraph.g2d.G2DSceneGraph;\r
60 import org.simantics.scenegraph.g2d.IG2DNode;\r
61 import org.simantics.scenegraph.g2d.nodes.BoundsNode;\r
62 import org.simantics.scenegraph.g2d.nodes.BranchPointNode;\r
63 import org.simantics.scenegraph.g2d.nodes.EdgeNode;\r
64 import org.simantics.scenegraph.g2d.nodes.GridNode;\r
65 import org.simantics.scenegraph.g2d.nodes.LinkNode;\r
66 import org.simantics.scenegraph.g2d.nodes.NavigationNode;\r
67 import org.simantics.scenegraph.g2d.nodes.PageBorderNode;\r
68 import org.simantics.scenegraph.g2d.nodes.RulerNode;\r
69 import org.simantics.scenegraph.g2d.nodes.SVGNode;\r
70 import org.simantics.scenegraph.g2d.nodes.ShapeNode;\r
71 import org.simantics.scenegraph.g2d.nodes.SingleElementNode;\r
72 import org.simantics.scenegraph.g2d.nodes.TransformNode;\r
73 import org.simantics.scenegraph.utils.NodeUtil;\r
74 import org.simantics.scenegraph.utils.NodeUtil.NodeProcedure;\r
75 \r
76 /**\r
77  * This view shows the contents of a 2D/3D canvas scenegraph through a tree\r
78  * viewer.\r
79  * \r
80  * <p>\r
81  * The viewer sources its scene graph input from the currently active workbench\r
82  * editor. It does not automatically track the active editor part but instead\r
83  * has to be refreshed manually (F5).\r
84  * </p>\r
85  * \r
86  * <p>\r
87  * References to actual scene graph nodes are only kept as {@link WeakReference}\r
88  * instances allowing them to be garbage collected if the scene graph is\r
89  * disposed of.\r
90  * </p>\r
91  * \r
92  * @author Tuukka Lehtonen\r
93  */\r
94 public class SceneGraphViewPart extends ViewPart {\r
95 \r
96     TreeViewer           tree;\r
97     LocalResourceManager resourceManager;\r
98     boolean              bootstrapped = false;\r
99     IContextActivation   contextActivation;\r
100     int                  currentNodeCount = 0;\r
101     boolean              linkToPart;\r
102     IWorkbenchPart       lastPart;\r
103     AttributeDialog      attributeDialog;\r
104 \r
105     final ImageDescriptor ROOT = ImageDescriptor.createFromURL(getClass().getResource("bullet_home.png"));\r
106     final ImageDescriptor CANVAS_BOUNDS = ImageDescriptor.createFromURL(getClass().getResource("application.png"));\r
107     final ImageDescriptor SHAPE = ImageDescriptor.createFromURL(getClass().getResource("shape_shadow.png"));\r
108     final ImageDescriptor NAVIGATION = ImageDescriptor.createFromURL(getClass().getResource("arrow_out_longer.png"));\r
109     final ImageDescriptor SVG = ImageDescriptor.createFromURL(getClass().getResource("script_code.png"));\r
110     final ImageDescriptor TRANSFORM = ImageDescriptor.createFromURL(getClass().getResource("arrow_nsew.png"));\r
111     final ImageDescriptor ELEMENT = ImageDescriptor.createFromURL(getClass().getResource("shape_handles.png"));\r
112     final ImageDescriptor PARENT = ImageDescriptor.createFromURL(getClass().getResource("share.png"));\r
113     final ImageDescriptor GRID = ImageDescriptor.createFromURL(getClass().getResource("border_all.png"));\r
114     final ImageDescriptor RULER = ImageDescriptor.createFromURL(getClass().getResource("text_ruler.png"));\r
115     final ImageDescriptor PAGE_BORDER = ImageDescriptor.createFromURL(getClass().getResource("page_white.png"));\r
116     final ImageDescriptor EDGE = ImageDescriptor.createFromURL(getClass().getResource("arrow_ew.png"));\r
117     final ImageDescriptor BRANCH_POINT = ImageDescriptor.createFromURL(getClass().getResource("bullet_black.png"));\r
118     final ImageDescriptor LINK = ImageDescriptor.createFromURL(getClass().getResource("link.png"));\r
119 \r
120     NodeProcedure<NodeProxy> nodeProcedure = new NodeProcedure<NodeProxy>() {\r
121         @Override\r
122         public NodeProxy execute(INode node, String id) {\r
123             return new NodeProxy(node, id);\r
124         }\r
125     };\r
126 \r
127     class ContentProvider implements ITreeContentProvider {\r
128 \r
129         @Override\r
130         public Object[] getChildren(Object parentElement) {\r
131             if (parentElement instanceof NodeProxy) {\r
132                 NodeProxy np = (NodeProxy) parentElement;\r
133                 INode n = np.getNode();\r
134                 if (n != null) {\r
135                     List<NodeProxy> children = NodeUtil.forChildren(n, nodeProcedure);\r
136                     return children.toArray();\r
137                 }\r
138             }\r
139             return new Object[0];\r
140         }\r
141 \r
142         @Override\r
143         public Object getParent(Object element) {\r
144             return null;\r
145         }\r
146 \r
147         @Override\r
148         public boolean hasChildren(Object element) {\r
149             if (element instanceof NodeProxy) {\r
150                 NodeProxy np = (NodeProxy) element;\r
151                 INode n = np.getNode();\r
152                 if (n != null)\r
153                     return NodeUtil.hasChildren(n);\r
154             }\r
155             return false;\r
156         }\r
157 \r
158         @Override\r
159         public Object[] getElements(Object inputElement) {\r
160             if (inputElement instanceof INode[]) {\r
161                 INode[] ns = (INode[]) inputElement;\r
162                 NodeProxy[] result = new NodeProxy[ns.length];\r
163                 for (int i = 0; i < ns.length; ++i)\r
164                     result[i] = new NodeProxy(ns[i], "root");\r
165                 return result;\r
166             }\r
167             if (inputElement instanceof INode) {\r
168                 INode n = (INode) inputElement;\r
169                 return new Object[] { new NodeProxy(n, "root") };\r
170             }\r
171             return new Object[0];\r
172         }\r
173 \r
174         @Override\r
175         public void dispose() {\r
176         }\r
177 \r
178         @Override\r
179         public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {\r
180         }\r
181     }\r
182 \r
183     private Image toImage(NodeProxy proxy) {\r
184         INode n = proxy.getNode();\r
185         if (n == null)\r
186             return null;\r
187 \r
188         if (n instanceof G2DSceneGraph) {\r
189             return resourceManager.createImage(ROOT);\r
190         } else if (n instanceof SingleElementNode) {\r
191             return resourceManager.createImage(ELEMENT);\r
192         } else if (n instanceof TransformNode) {\r
193             return resourceManager.createImage(TRANSFORM);\r
194         } else if (n instanceof NavigationNode) {\r
195             return resourceManager.createImage(NAVIGATION);\r
196         } else if (n instanceof BoundsNode) {\r
197             return resourceManager.createImage(CANVAS_BOUNDS);\r
198         } else if (n instanceof EdgeNode) {\r
199             return resourceManager.createImage(EDGE);\r
200         } else if (n instanceof BranchPointNode) {\r
201             return resourceManager.createImage(BRANCH_POINT);\r
202         } else if (n instanceof ShapeNode) {\r
203             return resourceManager.createImage(SHAPE);\r
204         } else if (n instanceof SVGNode) {\r
205             return resourceManager.createImage(SVG);\r
206         } else if (n instanceof G2DParentNode) {\r
207             return resourceManager.createImage(PARENT);\r
208         } else if (n instanceof GridNode) {\r
209             return resourceManager.createImage(GRID);\r
210         } else if (n instanceof RulerNode) {\r
211             return resourceManager.createImage(RULER);\r
212         } else if (n instanceof PageBorderNode) {\r
213             return resourceManager.createImage(PAGE_BORDER);\r
214         } else if (n instanceof LinkNode) {\r
215             return resourceManager.createImage(LINK);\r
216         }\r
217         return null;\r
218     }\r
219 \r
220     class InternalIdLabelProvider extends ColumnLabelProvider {\r
221         @Override\r
222         public void update(ViewerCell cell) {\r
223             NodeProxy proxy = (NodeProxy) cell.getElement();\r
224             cell.setText(proxy.getInternalId());\r
225             //cell.setImage(toImage(proxy));\r
226         }\r
227     }\r
228     class TypeLabelProvider extends ColumnLabelProvider {\r
229         @Override\r
230         public void update(ViewerCell cell) {\r
231             NodeProxy proxy = (NodeProxy) cell.getElement();\r
232             cell.setText(proxy.getTypeName());\r
233             //cell.setImage(toImage(proxy));\r
234         }\r
235     }\r
236     class IdLabelProvider extends ColumnLabelProvider {\r
237         @Override\r
238         public void update(ViewerCell cell) {\r
239             NodeProxy proxy = (NodeProxy) cell.getElement();\r
240             cell.setText(proxy.getId());\r
241             cell.setImage(toImage(proxy));\r
242         }\r
243     }\r
244     class LookupIdLabelProvider extends ColumnLabelProvider {\r
245         @Override\r
246         public void update(ViewerCell cell) {\r
247             NodeProxy proxy = (NodeProxy) cell.getElement();\r
248             INode node = proxy.getNode();\r
249             String lookupId = null;\r
250             if (node != null) {\r
251                 ILookupService lut = NodeUtil.tryGetLookupService(node);\r
252                 if (lut != null)\r
253                     lookupId = lut.lookupId(node);\r
254             }\r
255             cell.setText(lookupId != null ? lookupId : "");\r
256         }\r
257     }\r
258     class ZLabelProvider extends ColumnLabelProvider {\r
259         @Override\r
260         public void update(ViewerCell cell) {\r
261             NodeProxy proxy = (NodeProxy) cell.getElement();\r
262             INode node = proxy.getNode();\r
263             if (node instanceof IG2DNode) {\r
264                 IG2DNode n = (IG2DNode) node;\r
265                 cell.setText(String.valueOf(n.getZIndex()));\r
266             } else {\r
267                 cell.setText("-");\r
268             }\r
269         }\r
270     }\r
271 \r
272     @Override\r
273     public void createPartControl(Composite parent) {\r
274 \r
275         tree = new TreeViewer(parent, SWT.SINGLE | SWT.FULL_SELECTION);\r
276         resourceManager = new LocalResourceManager(JFaceResources.getResources(), tree.getTree());\r
277 \r
278         TreeColumnLayout ad = new TreeColumnLayout();\r
279         parent.setLayout(ad);\r
280 \r
281         //tree.getTree().setLayout(new FillLayout());\r
282         tree.setContentProvider(new ContentProvider());\r
283         tree.getTree().setHeaderVisible(true);\r
284         //tree.getTree().setLinesVisible(true);\r
285         tree.setUseHashlookup(true);\r
286         tree.setAutoExpandLevel(3);\r
287 \r
288         TreeViewerColumn nameColumn = new TreeViewerColumn(tree, SWT.LEFT);\r
289         TreeViewerColumn typeColumn = new TreeViewerColumn(tree, SWT.LEFT);\r
290         TreeViewerColumn idColumn = new TreeViewerColumn(tree, SWT.LEFT);\r
291         TreeViewerColumn lookupIdColumn = new TreeViewerColumn(tree, SWT.LEFT);\r
292         TreeViewerColumn zColumn = new TreeViewerColumn(tree, SWT.LEFT);\r
293 \r
294         nameColumn.setLabelProvider(new IdLabelProvider());\r
295         typeColumn.setLabelProvider(new TypeLabelProvider());\r
296         idColumn.setLabelProvider(new InternalIdLabelProvider());\r
297         lookupIdColumn.setLabelProvider(new LookupIdLabelProvider());\r
298         zColumn.setLabelProvider(new ZLabelProvider());\r
299 \r
300         nameColumn.getColumn().setText("Name");\r
301         nameColumn.getColumn().setWidth(20);\r
302         ad.setColumnData(nameColumn.getColumn(), new ColumnWeightData(80, 100));\r
303         typeColumn.getColumn().setText("Type");\r
304         typeColumn.getColumn().setWidth(20);\r
305         ad.setColumnData(typeColumn.getColumn(), new ColumnWeightData(20, 120));\r
306         idColumn.getColumn().setText("ID");\r
307         idColumn.getColumn().setWidth(20);\r
308         ad.setColumnData(idColumn.getColumn(), new ColumnWeightData(10, 50));\r
309         lookupIdColumn.getColumn().setText("Lookup ID");\r
310         lookupIdColumn.getColumn().setWidth(20);\r
311         ad.setColumnData(lookupIdColumn.getColumn(), new ColumnWeightData(50, 100));\r
312         zColumn.getColumn().setText("Z");\r
313         zColumn.getColumn().setWidth(70);\r
314         ad.setColumnData(zColumn.getColumn(), new ColumnWeightData(10, 70));\r
315 \r
316         tree.addSelectionChangedListener(new ISelectionChangedListener() {\r
317             @Override\r
318             public void selectionChanged(SelectionChangedEvent event) {\r
319                 updateContentDescription();\r
320             }\r
321         });\r
322         tree.addDoubleClickListener(new IDoubleClickListener() {\r
323             @Override\r
324             public void doubleClick(DoubleClickEvent event) {\r
325                 openAttributeDialog();\r
326             }\r
327         });\r
328 \r
329         contextActivation = ((IContextService) getSite()\r
330                 .getService(IContextService.class))\r
331                 .activateContext("org.simantics.scenegraph.viewer");\r
332 \r
333         ICommandService commandService = (ICommandService) PlatformUI.getWorkbench().getService(ICommandService.class);\r
334         Command command = commandService.getCommand(LinkToActiveWorkbenchPartHandler.COMMAND);\r
335         State state = command.getState(LinkToActiveWorkbenchPartHandler.STATE);\r
336         this.linkToPart = Boolean.TRUE.equals(state.getValue());\r
337 \r
338         // No need to remove this listener, the part service is local to this site.\r
339         IPartService partService = (IPartService) getSite().getService(IPartService.class);\r
340         partService.addPartListener(partListener);\r
341     }\r
342 \r
343     @Override\r
344     public void dispose() {\r
345         closeAttributeDialog();\r
346     }\r
347 \r
348     protected void openAttributeDialog() {\r
349         if (attributeDialog != null) {\r
350             Shell shell = attributeDialog.getShell();\r
351             if (shell == null || shell.isDisposed())\r
352                 attributeDialog = null;\r
353         }\r
354         if (attributeDialog == null) {\r
355             attributeDialog = new AttributeDialog(getSite().getShell(), tree);\r
356             attributeDialog.setBlockOnOpen(false);\r
357             attributeDialog.open();\r
358         }\r
359     }\r
360 \r
361     protected void closeAttributeDialog() {\r
362         if (attributeDialog != null) {\r
363             attributeDialog.close();\r
364             attributeDialog = null;\r
365         }\r
366     }\r
367 \r
368     IPartListener2 partListener = new IPartListener2() {\r
369         @Override\r
370         public void partVisible(IWorkbenchPartReference partRef) {\r
371         }\r
372         @Override\r
373         public void partOpened(IWorkbenchPartReference partRef) {\r
374         }\r
375         @Override\r
376         public void partInputChanged(IWorkbenchPartReference partRef) {\r
377         }\r
378         @Override\r
379         public void partHidden(IWorkbenchPartReference partRef) {\r
380         }\r
381         @Override\r
382         public void partDeactivated(IWorkbenchPartReference partRef) {\r
383         }\r
384         @Override\r
385         public void partClosed(IWorkbenchPartReference partRef) {\r
386             if (linkToPart) {\r
387                 IWorkbenchPart part = partRef.getPart(false);\r
388                 if (part != null)\r
389                     refresh(null);\r
390             }\r
391         }\r
392         @Override\r
393         public void partBroughtToTop(IWorkbenchPartReference partRef) {\r
394         }\r
395         @Override\r
396         public void partActivated(IWorkbenchPartReference partRef) {\r
397             if (linkToPart) {\r
398                 IWorkbenchPart part = partRef.getPart(false);\r
399                 if (part != null) {\r
400                     if (part != lastPart) {\r
401                         refresh(part);\r
402                     }\r
403                 }\r
404             }\r
405         }\r
406     };\r
407 \r
408     @Override\r
409     public void setFocus() {\r
410         tree.getTree().setFocus();\r
411         if (!bootstrapped) {\r
412             bootstrapped = true;\r
413             refresh();\r
414         }\r
415     }\r
416 \r
417     protected void refresh() {\r
418         IWorkbenchPart part = null;\r
419         try {\r
420             IWorkbenchWindow window = getSite().getWorkbenchWindow();\r
421             if (window == null)\r
422                 return;\r
423             IWorkbenchPage page = window.getActivePage();\r
424             if (page == null)\r
425                 return;\r
426             part = page.getActiveEditor();\r
427             if (part == null)\r
428                 return;\r
429         } finally {\r
430             if (part == null) {\r
431                 setContentDescription("No scene graph nodes available.");\r
432                 // TODO: Show info page instead of tree view.\r
433             }\r
434         }\r
435 \r
436         refresh(part);\r
437     }\r
438 \r
439     /**\r
440      * @param part <code>null</code> to reset the view to a blank state.\r
441      * @return\r
442      */\r
443     protected boolean refresh(IWorkbenchPart part) {\r
444         boolean foundInput = true;\r
445         try {\r
446             Object obj = null;\r
447             if (part != null) {\r
448                 obj = part.getAdapter(INode[].class);\r
449                 if (obj == null)\r
450                     obj = part.getAdapter(INode.class);\r
451             }\r
452 \r
453             if (obj != null) {\r
454                 TreePath[] expanded = tree.getExpandedTreePaths();\r
455                 tree.setInput(obj);\r
456                 tree.setExpandedTreePaths(expanded);\r
457                 this.currentNodeCount = countNodes(obj);\r
458                 updateContentDescription();\r
459                 foundInput = true;\r
460             }\r
461             lastPart = part;\r
462             return foundInput;\r
463         } finally {\r
464             if (!foundInput) {\r
465                 setContentDescription("No scene graph nodes available.");\r
466                 // TODO: Show info page instead of tree view.\r
467             }\r
468         }\r
469     }\r
470 \r
471     private void updateContentDescription() {\r
472         StringBuilder desc = new StringBuilder();\r
473         desc.append(currentNodeCount + " nodes in total.");\r
474 \r
475         IStructuredSelection ss = (IStructuredSelection) tree.getSelection();\r
476         Object obj = ss.getFirstElement();\r
477         if (obj instanceof NodeProxy) {\r
478             NodeProxy np = (NodeProxy) obj;\r
479             INode n = np.getNode();\r
480             if (n != null) {\r
481                 int depth = NodeUtil.getDepth(n);\r
482                 desc.append(" Selection ");\r
483                 desc.append("at depth ");\r
484                 desc.append(depth);\r
485                 desc.append(".");\r
486             }\r
487         }\r
488 \r
489         setContentDescription(desc.toString());\r
490     }\r
491 \r
492     private int countNodes(Object obj) {\r
493         if (obj instanceof INode) {\r
494             INode n = (INode) obj;\r
495             return NodeUtil.countTreeNodes(n);\r
496         } else if (obj instanceof INode[]) {\r
497             INode[] ns = (INode[]) obj;\r
498             int result = 0;\r
499             for (INode n : ns)\r
500                 result += NodeUtil.countTreeNodes(n);\r
501             return result;\r
502         }\r
503         return 0;\r
504     }\r
505 \r
506     void copySelectionToClipboard() {\r
507         IStructuredSelection selection = (IStructuredSelection) tree.getSelection();\r
508         Object obj = selection.getFirstElement();\r
509         if (!(obj instanceof NodeProxy))\r
510             return;\r
511 \r
512         NodeProxy np = (NodeProxy) obj;\r
513         INode n = np.getNode();\r
514         if (n == null)\r
515             return;\r
516 \r
517         ByteArrayOutputStream bytes = new ByteArrayOutputStream(100000);\r
518         PrintStream stream = new PrintStream(bytes);\r
519         NodeUtil.printSceneGraph(stream, 0, n);\r
520         String textData = new String(bytes.toByteArray());\r
521         if (textData.isEmpty())\r
522             return;\r
523 \r
524         Clipboard clipboard = new Clipboard(tree.getControl().getDisplay());\r
525         TextTransfer textTransfer = TextTransfer.getInstance();\r
526         Transfer[] transfers = new Transfer[]{textTransfer};\r
527         Object[] data = new Object[]{textData};\r
528         clipboard.setContents(data, transfers);\r
529         clipboard.dispose();\r
530     }\r
531 \r
532     void collapseAll() {\r
533         for (Object obj : tree.getExpandedElements()) {\r
534             tree.setExpandedState(obj, false);\r
535         }\r
536     }\r
537 \r
538     void expandSelectedNode() {\r
539         IStructuredSelection ss = (IStructuredSelection) tree.getSelection();\r
540         for (Object obj : ss.toList()) {\r
541             tree.expandToLevel(obj, TreeViewer.ALL_LEVELS);\r
542         }\r
543     }\r
544 \r
545     public void linkToActiveWorkbenchPart(boolean value) {\r
546         this.linkToPart = value;\r
547     }\r
548 \r
549 }\r