PixelPerfectTree.java revision c1db1ab270b953da579e657de11d4486a49e1c1e
1bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev/*
2bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev * Copyright (C) 2010 The Android Open Source Project
3bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev *
4bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev * Licensed under the Apache License, Version 2.0 (the "License");
5bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev * you may not use this file except in compliance with the License.
6bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev * You may obtain a copy of the License at
7bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev *
8bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev *      http://www.apache.org/licenses/LICENSE-2.0
9bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev *
10bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev * Unless required by applicable law or agreed to in writing, software
11bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev * distributed under the License is distributed on an "AS IS" BASIS,
12bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev * See the License for the specific language governing permissions and
14bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev * limitations under the License.
15bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev */
16bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev
17bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrevpackage com.android.hierarchyvieweruilib;
18bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev
19bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrevimport com.android.ddmuilib.ImageLoader;
20bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrevimport com.android.hierarchyviewerlib.ComponentRegistry;
21bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrevimport com.android.hierarchyviewerlib.device.ViewNode;
22bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrevimport com.android.hierarchyviewerlib.models.PixelPerfectModel;
23bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrevimport com.android.hierarchyviewerlib.models.PixelPerfectModel.ImageChangeListener;
24bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev
25bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrevimport org.eclipse.jface.viewers.ILabelProvider;
26bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrevimport org.eclipse.jface.viewers.ILabelProviderListener;
27bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrevimport org.eclipse.jface.viewers.ITreeContentProvider;
28c1db1ab270b953da579e657de11d4486a49e1c1eKonstantin Lopyrevimport org.eclipse.jface.viewers.TreeSelection;
29bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrevimport org.eclipse.jface.viewers.TreeViewer;
30bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrevimport org.eclipse.jface.viewers.Viewer;
31bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrevimport org.eclipse.swt.SWT;
32bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrevimport org.eclipse.swt.events.SelectionEvent;
33bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrevimport org.eclipse.swt.events.SelectionListener;
34bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrevimport org.eclipse.swt.graphics.Image;
35c1db1ab270b953da579e657de11d4486a49e1c1eKonstantin Lopyrevimport org.eclipse.swt.layout.FillLayout;
36bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrevimport org.eclipse.swt.widgets.Composite;
37bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrevimport org.eclipse.swt.widgets.Display;
38bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrevimport org.eclipse.swt.widgets.Tree;
39bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrevimport org.eclipse.swt.widgets.TreeColumn;
40bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev
41bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrevimport java.util.List;
42bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev
43c1db1ab270b953da579e657de11d4486a49e1c1eKonstantin Lopyrevpublic class PixelPerfectTree extends Composite implements ImageChangeListener, SelectionListener {
44bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev
45bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev    private TreeViewer treeViewer;
46bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev
47bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev    private Tree tree;
48bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev
49bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev    private PixelPerfectModel model;
50bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev
51bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev    private Image folderImage;
52bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev
53bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev    private Image fileImage;
54bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev
55bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev    private class ContentProvider implements ITreeContentProvider, ILabelProvider {
56bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev        public Object[] getChildren(Object element) {
57bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev            if (element instanceof ViewNode) {
58bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev                List<ViewNode> children = ((ViewNode) element).children;
59bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev                return children.toArray(new ViewNode[children.size()]);
60bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev            }
61bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev            return null;
62bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev        }
63bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev
64bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev        public Object getParent(Object element) {
65bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev            if (element instanceof ViewNode) {
66bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev                return ((ViewNode) element).parent;
67bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev            }
68bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev            return null;
69bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev        }
70bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev
71bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev        public boolean hasChildren(Object element) {
72bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev            if (element instanceof ViewNode) {
73bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev                return ((ViewNode) element).children.size() != 0;
74bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev            }
75bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev            return false;
76bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev        }
77bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev
78bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev        public Object[] getElements(Object element) {
79bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev            if (element instanceof PixelPerfectModel) {
80bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev                ViewNode viewNode = ((PixelPerfectModel) element).getViewNode();
81bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev                if (viewNode == null) {
82bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev                    return new Object[0];
83bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev                }
84bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev                return new Object[] {
85bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev                    viewNode
86bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev                };
87bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev            }
88bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev            return new Object[0];
89bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev        }
90bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev
91bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev        public void dispose() {
92bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev            // pass
93bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev        }
94bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev
95bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev        public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
96bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev            // pass
97bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev        }
98bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev
99bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev        public Image getImage(Object element) {
100bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev            if (element instanceof ViewNode) {
101bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev                if (hasChildren(element)) {
102bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev                    return folderImage;
103bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev                }
104bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev                return fileImage;
105bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev            }
106bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev            return null;
107bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev        }
108bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev
109bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev        public String getText(Object element) {
110bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev            if (element instanceof ViewNode) {
111bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev                return ((ViewNode) element).name;
112bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev            }
113bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev            return null;
114bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev        }
115bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev
116bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev        public void addListener(ILabelProviderListener listener) {
117bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev            // pass
118bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev        }
119bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev
120bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev        public boolean isLabelProperty(Object element, String property) {
121bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev            // pass
122bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev            return false;
123bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev        }
124bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev
125bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev        public void removeListener(ILabelProviderListener listener) {
126bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev            // pass
127bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev        }
128bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev    }
129bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev
130bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev    public PixelPerfectTree(Composite parent) {
131c1db1ab270b953da579e657de11d4486a49e1c1eKonstantin Lopyrev        super(parent, SWT.NONE);
132c1db1ab270b953da579e657de11d4486a49e1c1eKonstantin Lopyrev        setLayout(new FillLayout());
133c1db1ab270b953da579e657de11d4486a49e1c1eKonstantin Lopyrev        treeViewer = new TreeViewer(this, SWT.SINGLE);
134bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev        treeViewer.setAutoExpandLevel(TreeViewer.ALL_LEVELS);
135bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev
136bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev        tree = treeViewer.getTree();
137bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev        TreeColumn col = new TreeColumn(tree, SWT.LEFT);
138bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev        col.setText("Name");
139bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev        col.pack();
140bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev        tree.setHeaderVisible(true);
141bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev        tree.addSelectionListener(this);
142bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev
143bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev        loadResources();
144bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev
145bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev        model = ComponentRegistry.getPixelPerfectModel();
146bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev        ContentProvider contentProvider = new ContentProvider();
147bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev        treeViewer.setContentProvider(contentProvider);
148bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev        treeViewer.setLabelProvider(contentProvider);
149bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev        treeViewer.setInput(model);
150bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev        model.addImageChangeListener(this);
151bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev
152bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev    }
153bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev
154bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev    public void loadResources() {
155bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev        ImageLoader loader = ImageLoader.getDdmUiLibLoader();
156bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev        fileImage = loader.loadImage("file.png", Display.getDefault());
157bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev
158bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev        folderImage = loader.loadImage("folder.png", Display.getDefault());
159bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev    }
160bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev
161c1db1ab270b953da579e657de11d4486a49e1c1eKonstantin Lopyrev    @Override
162c1db1ab270b953da579e657de11d4486a49e1c1eKonstantin Lopyrev    public void dispose() {
163c1db1ab270b953da579e657de11d4486a49e1c1eKonstantin Lopyrev        super.dispose();
164bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev        fileImage.dispose();
165bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev        folderImage.dispose();
166bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev    }
167bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev
168c1db1ab270b953da579e657de11d4486a49e1c1eKonstantin Lopyrev    @Override
169c1db1ab270b953da579e657de11d4486a49e1c1eKonstantin Lopyrev    public boolean setFocus() {
170c1db1ab270b953da579e657de11d4486a49e1c1eKonstantin Lopyrev        return tree.setFocus();
171bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev    }
172bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev
173bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev    public void imageLoaded() {
174bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev        Display.getDefault().asyncExec(new Runnable() {
175bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev            public void run() {
176bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev                treeViewer.refresh();
177bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev                treeViewer.expandAll();
178bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev            }
179bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev        });
180bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev    }
181bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev
182bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev    public void imageChanged() {
183bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev        // pass
184bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev    }
185bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev
186bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev    public void crosshairMoved() {
187bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev        // pass
188bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev    }
189bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev
190bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev    public void selectionChanged() {
191bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev        // pass
192bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev    }
193bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev
194bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev    public void focusChanged() {
195bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev        imageLoaded();
196bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev    }
197bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev
198bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev    public void widgetDefaultSelected(SelectionEvent e) {
199bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev        // pass
200bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev    }
201bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev
202bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev    public void widgetSelected(SelectionEvent e) {
203c1db1ab270b953da579e657de11d4486a49e1c1eKonstantin Lopyrev        // To combat phantom selection...
204c1db1ab270b953da579e657de11d4486a49e1c1eKonstantin Lopyrev        if (((TreeSelection) treeViewer.getSelection()).isEmpty()) {
205c1db1ab270b953da579e657de11d4486a49e1c1eKonstantin Lopyrev            model.setSelected(null);
206c1db1ab270b953da579e657de11d4486a49e1c1eKonstantin Lopyrev        } else {
207c1db1ab270b953da579e657de11d4486a49e1c1eKonstantin Lopyrev            model.setSelected((ViewNode) e.item.getData());
208c1db1ab270b953da579e657de11d4486a49e1c1eKonstantin Lopyrev        }
209c1db1ab270b953da579e657de11d4486a49e1c1eKonstantin Lopyrev    }
210c1db1ab270b953da579e657de11d4486a49e1c1eKonstantin Lopyrev
211c1db1ab270b953da579e657de11d4486a49e1c1eKonstantin Lopyrev    public void zoomChanged() {
212c1db1ab270b953da579e657de11d4486a49e1c1eKonstantin Lopyrev        // pass
213bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev    }
214bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev
215bc494ce06410728bef8a6fa25f3da361cb66df79Konstantin Lopyrev}
216