source: java/main/src/main/java/com/framsticks/gui/TreeNode.java @ 84

Last change on this file since 84 was 84, checked in by psniegowski, 11 years ago

HIGHLIGHTS:

  • simplification of entities management model
  • cleanup around params (improve hierarchy)
  • migrate from JUnit to TestNG
  • introduce FEST to automatically test GUI
  • improve slider control
  • loosen synchronization between gui tree and backend representation
  • and many other bug fixes

NOTICE:

  • a great many of lines is changed only because of substituting spaces with tabs

CHANGELOG (oldest changes at the bottom):

Some cleaning after fix found.

Fix bug with tree.

More changes with TreeNodes?.

Finally fix issue with tree.

Improve gui tree management.

Decouple update of values from fetch request in gui.

Minor changes.

Minor changes.

Minor change.

Change Path construction wording.

More fixes to SliderControl?.

Fix SliderControl?.

Fix SliderControl?.

Minor improvement.

Several changes.

Make NumberParam? a generic class.

Add robot to the gui test.

Setup common testing logging configuration.

Remove Parameters class.

Remove entityOwner from Parameters.

Move name out from Parameters class.

Move configuration to after the construction.

Simplify observers and endpoints.

Remove superfluous configureEntity overrides.

Add dependency on fest-swing-testng.

Use FEST for final print test.

Use FEST for more concise and readable assertions.

Divide test of F0Parser into multiple methods.

Migrate to TestNG

Minor change.

Change convention from LOGGER to log.

Fix reporting of errors during controls filling.

Bound maximal height of SliderControl?.

Minor improvements.

Improve tooltips for controls.

Also use Delimeted in more places.

Move static control utilities to Gui.

Rename package gui.components to controls.

Some cleaning in controls.

Improve Param classes placing.

Move ValueParam?, PrimitiveParam? and CompositeParam? one package up.

Improve ParamBuilder?.

Move getDef to ValueParam? and PrimitiveParam?.

Move getMax and getDef to ValueParam?.

Move getMin to ValueParam?.

Upgrade to laters apache commons versions.

Use filterInstanceof extensively.

Add instanceof filters.

Make ValueParam? in many places of Param.

Place assertions about ValueParam?.

Add ValueParam?

Rename ValueParam? to PrimitiveParam?

Minor changes.

Several improvements to params types.

Add NumberParam?.

Add TextControl? component.

Add .swp files to .gitignore

Greatly improved slider component.

Some improvements.

Make Param.reassign return also a state.

Add IterableIterator?.

Several changes.

  • Move util classes to better packages.
  • Remove warnings from eclim.

Several improvements.

Fix bug with BooleanParam?.

Some experiments with visualization.

Another fix to panel management.

Improve panel management.

Some refactorization around panels.

Add root class for panel.

File size: 13.5 KB
Line 
1package com.framsticks.gui;
2
3import com.framsticks.communication.Subscription;
4import com.framsticks.communication.util.LoggingStateCallback;
5import com.framsticks.core.ListChange;
6import com.framsticks.core.Path;
7import com.framsticks.gui.controls.ValueControl;
8import com.framsticks.gui.view.TreeCellRenderer;
9import com.framsticks.params.AccessInterface;
10import com.framsticks.params.CompositeParam;
11import com.framsticks.params.FramsClass;
12import com.framsticks.params.types.*;
13import com.framsticks.remote.*;
14import com.framsticks.util.lang.Casting;
15import com.framsticks.util.swing.TooltipConstructor;
16import com.framsticks.util.dispatching.Future;
17import com.framsticks.util.Logging;
18import com.framsticks.util.StateFunctor;
19import org.apache.log4j.Logger;
20
21import javax.swing.tree.DefaultMutableTreeNode;
22import java.util.HashMap;
23import java.util.LinkedList;
24import java.util.List;
25import java.util.Map;
26import static com.framsticks.util.lang.Containers.filterInstanceof;
27import com.framsticks.util.swing.TreeNodeUtils;
28
29/**
30 * @author Piotr Sniegowski
31 */
32@SuppressWarnings("serial")
33public class TreeNode extends DefaultMutableTreeNode implements NodeListener {
34
35        private static final Logger log = Logger.getLogger(TreeNode.class.getName());
36
37        Panel panel;
38
39        final protected Frame frame;
40        final protected EndpointAtFrame endpoint;
41
42        final protected Map<EventParam, Subscription> userSubscriptions = new HashMap<EventParam, Subscription>();
43        protected Map<ValueControl, Object> localChanges = null;
44        protected String tooltip;
45        protected String name;
46        protected final String paramId;
47        protected String iconName;
48        protected Path path;
49
50        public TreeNode(EndpointAtFrame endpoint, final Path path) {
51                this.frame = endpoint.getFrame();
52                assert frame.isActive();
53                this.paramId = path.getLastElement();
54                this.name = this.paramId;
55                log.debug("creating treenode " + name + ": " + path);
56                this.path = path;
57                this.endpoint = endpoint;
58
59                iconName = TreeCellRenderer.findIconName(name, path.getTextual());
60                tooltip = "?";
61                path.getInstance().invokeLater(new Runnable() {
62                        @Override
63                        public void run() {
64                                updateDescriptions(path);
65                        }
66                });
67        }
68
69        public void fetch(final Path p) {
70                assert p.getInstance().isActive();
71                log.debug("fetching: " + p);
72                p.getInstance().fetchValues(p, new StateFunctor() {
73                        @Override
74                        public void call(Exception e) {
75                                //TODO removing should stay here
76                                // reactForFetchResult(p, e);
77                        }
78                });
79        }
80
81        protected void reactForFetchResult(final Path p, Exception e) {
82                assert p.getInstance().isActive();
83                if (Logging.log(log, "fetch", TreeNode.this, e)) {
84                        frame.invokeLater(new Runnable() {
85                                @Override
86                                public void run() {
87                                        log.debug("removing node from tree: " + p);
88                                        frame.treeModel.removeNodeFromParent(TreeNode.this);
89                                }
90                        });
91                        return;
92                }
93                updateChildren(p);
94                frame.invokeLater(new Runnable() {
95                        @Override
96                        public void run() {
97                                //TODO maybe this should be called from some better place
98                                useOrCreatePanel();
99                        }
100                });
101        }
102
103        protected void postUpdatePath(final Path newPath) {
104                assert !frame.isActive();
105                /** TODO those two actions could be merged into single closure */
106                frame.invokeLater(new Runnable() {
107                        @Override
108                        public void run() {
109                                updatePath(newPath);
110                        }
111                });
112                updateDescriptions(newPath);
113        }
114
115        protected void updatePath(Path newPath) {
116                assert frame.isActive();
117                if (!path.isResolved()) {
118                        path = newPath;
119                        log.debug("updated treenode's path: " + path);
120                        return;
121                }
122                if (path.matches(newPath)) {
123                        return;
124                }
125                log.debug("removing all children due to path update");
126                this.removeAllChildren();
127                frame.treeModel.nodeStructureChanged(this);
128        }
129
130        public Iterable<TreeNode> childrenIterable() {
131                return filterInstanceof(TreeNodeUtils.children(TreeNode.this, frame.treeModel), TreeNode.class);
132        }
133
134        /** Update children, by removing non existing and adding new ones.
135         */
136        protected void updateChildren(final Path p) {
137                assert p.getInstance().isActive();
138                log.debug("updating children of " + this);
139                AccessInterface access = p.getInstance().bindAccess(p.getTop());
140                if (access == null) {
141                        return;
142                }
143                final List<Path> childrenPaths = new LinkedList<Path>();
144                /**Prepare path for each child.*/
145                for (CompositeParam param : filterInstanceof(access.getParams(), CompositeParam.class)) {
146                        childrenPaths.add(p.appendParam(param).tryFindResolution());
147                }
148
149                /**If some child were found, update in frame context.*/
150                if (childrenPaths.size() > 0) {
151                        frame.invokeLater(new Runnable() {
152                                @Override
153                                public void run() {
154                                        // TreePath treePath = frame.startChange();
155                                        updatePath(p);
156                                        Map<String, TreeNode> current = new HashMap<String, TreeNode>();
157                                        for (TreeNode n : childrenIterable()) {
158                                                current.put(n.path.getLastElement(), n);
159                                        }
160                                        assert current.size() == TreeNode.this.getChildCount();
161                                        assert TreeNode.this.getChildCount() == frame.treeModel.getChildCount(TreeNode.this);
162                                        log.trace("current " + TreeNode.this.getChildCount() + " children: " + current.values());
163                                        for (Path childPath : childrenPaths) {
164                                                String e = childPath.getLastElement();
165                                                if (current.containsKey(e)) {
166                                                        current.remove(e);
167                                                        continue;
168                                                }
169                                                log.debug("update: treenode for " + p);
170                                                TreeNode childNode = new TreeNode(endpoint, childPath);
171
172                                                frame.addNode(childNode, TreeNode.this);
173                                        }
174                                        for (TreeNode n : current.values()) {
175                                                frame.treeModel.removeNodeFromParent(n);
176                                        }
177                                        log.debug("updated children of " + TreeNode.this + ": " + TreeNode.this.getChildCount());
178                                }
179                        });
180                } else {
181                        log.debug("no children in " + TreeNode.this);
182                }
183        }
184
185        public void select() {
186                final Path p = path;
187
188                p.getInstance().invokeLater(new Runnable() {
189                        @Override
190                        public void run() {
191                                final Path updated = (p.isResolved()) ? p : p.tryFindResolution();
192                                if (updated.isResolved()) {
193                                        Logging.log(log, "update", updated, null);
194                                        fetch(updated);
195                                        postUpdatePath(updated);
196                                        return;
197                                }
198                                p.getInstance().resolve(updated, new Future<Path>() {
199                                        @Override
200                                        public void result(final Path result, Exception e) {
201                                                if (Logging.log(log, "resolve and select", TreeNode.this, e)) {
202                                                        return;
203                                                }
204
205                                                fetch(result);
206                                                postUpdatePath(result);
207                                        }
208                                });
209                        }
210                });
211
212        }
213
214        @Override
215        public String toString() {
216                return path.toString();
217        }
218
219        public final Panel getPanel() {
220                assert frame.isActive();
221                return panel;
222        }
223
224        protected void updateDescriptions(Path p) {
225                assert p.getInstance().isActive();
226
227                if (!p.isResolved()) {
228                        return;
229                }
230                AccessInterface access = p.getInstance().bindAccess(p);
231                if (access == null) {
232                        return;
233                }
234
235                final String tooltip = new TooltipConstructor()
236                        .append("frams", access.getId())
237                        .append("java", p.getTopObject().getClass().getCanonicalName())
238                        .append("access", access.getClass().getSimpleName())
239                        .append("name", name)
240                        .append("id", paramId)
241                        .append("object", Integer.toHexString(System.identityHashCode(this)))
242                        .build()
243                        ;
244
245                StringParam nameParam = Casting.tryCast(StringParam.class, access.getParam("name"));
246                final String name = (nameParam != null ? access.get(nameParam, String.class) : path.getTop().getParam().getId());
247
248                frame.invokeLater(new Runnable() {
249                        @Override
250                        public void run() {
251                                TreeNode.this.tooltip = tooltip;
252                                TreeNode.this.name = name;
253
254                                log.debug("updated descriptions for " + path + ": " + name);
255                        }
256                });
257        }
258
259/*
260        public void updateData() {
261                assert browser.isActive();
262                final Node node = getNode();
263                browser.manager.invokeLater(new Runnable() {
264                        @Override
265                        public void run() {
266                                node.fetchValues(new StateFunctor() {
267                                        @Override
268                                        public void call(Exception e) {
269                                                if (e != null) {
270                                                        return;
271                                                }
272                                                browser.invokeLater(new Runnable() {
273                                                        @Override
274                                                        public void run() {
275                                                                assert browser.isActive();
276                                                                if (panel.getCurrentTreeNode() == TreeNode.this) {
277                                                                        panel.refreshComponents();
278                                                                }
279
280                                                                browser.tree.repaint();
281                                                                panel.refreshComponents();
282                                                        }
283                                                });
284                                        }
285                                });
286                        }
287                });
288        }
289*/
290
291        public void showPanel() {
292                assert frame.isActive();
293                assert panel != null;
294                frame.showPanel(panel);
295        }
296
297        public void fillPanelWithValues() {
298                assert frame.isActive();
299                if (panel == null) {
300                        return;
301                }
302                final Path p = path;
303                assert p.isResolved();
304                panel.setCurrentTreeNode(this);
305                p.getInstance().invokeLater(new Runnable() {
306                        @Override
307                        public void run() {
308                                AccessInterface access = p.getInstance().bindAccess(p);
309                                panel.pullValuesFromLocalToUser(access);
310
311                                frame.invokeLater(new Runnable() {
312                                        @Override
313                                        public void run() {
314                                                showPanel();
315                                        }
316                                });
317                        }
318                });
319
320        }
321
322        public void useOrCreatePanel() {
323                assert frame.isActive();
324                if (panel != null) {
325                        log.trace("panel is already attached: " + path);
326                        fillPanelWithValues();
327                        return;
328                }
329                if (!path.isResolved()) {
330                        log.trace("path is not resolved: " + path);
331                        return;
332                }
333
334                CompositeParam param = path.getTop().getParam();
335                panel = endpoint.findPanel(param.computeAccessId());
336                if (panel != null) {
337                        log.debug("found prepared panel for: " + path);
338                        fillPanelWithValues();
339                        return;
340                }
341                final Path p = path;
342                log.debug("preparing panel: " + p);
343                p.getInstance().invokeLater(new Runnable() {
344                        @Override
345                        public void run() {
346                                assert p.getInstance().isActive();
347                                final CompositeParam param = p.getTop().getParam();
348                                final FramsClass framsClass = p.getInstance().getInfoFromCache(param.getContainedTypeName());
349                                frame.invokeLater(new Runnable() {
350                                        @Override
351                                        public void run() {
352                                                panel = endpoint.preparePanel(param, framsClass);
353                                                fillPanelWithValues();
354                                        }
355                                });
356                        }
357                });
358        }
359
360        public String getTooltip() {
361                assert frame.isActive();
362                return tooltip;
363        }
364
365
366
367
368        /*public void subscribe(final EventParam eventParam) {
369                assert browser.isActive();
370                if (hasSubscribed(eventParam)) {
371                        log.error(eventParam + " is already subscribed for " + this);
372                        return;
373                }
374                Node node = getNode();
375                node.getConnection().subscribe(node.getPath() + "/" + eventParam.getId(), new SubscriptionCallback() {
376                        @Override
377                        public EventCallback subscribed(final Subscription subscription) {
378                                if (subscription == null) {
379                                        log.error("subscription failed");
380                                        return null;
381                                }
382                                if (hasSubscribed(eventParam)) {
383                                        //abort subscription
384                                        return null;
385                                }
386                                userSubscriptions.put(eventParam, subscription);
387                                log.debug("subscription succeeded: " + subscription);
388                                subscription.setDispatcher(browser);
389                                return new EventCallback() {
390                                        @Override
391                                        public void call(SourceInterface content) {
392                                                assert browser.isActive();
393                                                log.info("event " + subscription + " occurred");
394                                        }
395                                };
396                        }
397                });
398
399        }*/
400
401        public boolean hasSubscribed(EventParam param) {
402                assert frame.isActive();
403                return userSubscriptions.containsKey(param);
404        }
405
406        public void unsubscribe(EventParam eventParam) {
407                assert frame.isActive();
408                if (!hasSubscribed(eventParam)) {
409                        log.error("could not unsubscribe from " + eventParam);
410                        return;
411                }
412                userSubscriptions.get(eventParam).unsubscribe(new LoggingStateCallback(log, "unsubscribed " + eventParam));
413                userSubscriptions.remove(eventParam);
414        }
415
416/*
417
418        @Override
419        public void onChildChange(final Child child, ListChange.Action action) {
420                assert browser.manager.isActive();
421
422                switch (action) {
423                        case Remove: {
424                                Dispatching.invokeDispatch(browser, browser.manager, new Runnable() {
425                                        @Override
426                                        public void run() {
427                                                assert browser.manager.isActive();
428                                                final TreeNode treeNode = (TreeNode) child.getUserObject();
429                                                if (treeNode == null) {
430                                                        log.error("child " + child + " had no tree node attached");
431                                                        return;
432                                                }
433                                                browser.invokeLater(new Runnable() {
434                                                        @Override
435                                                        public void run() {
436                                                                assert browser.isActive();
437                                                                TreePath path = browser.startChange();
438                                                                //assert treeNode.getParent() == TreeNode.this;
439                                                                if (treeNode.getParent() != null) {
440                                                                        browser.treeModel.removeNodeFromParent(treeNode);
441                                                                }
442                                                                //remove(treeNode);
443                                                                browser.markNodeChanged(TreeNode.this, path);
444                                                        }
445                                                });
446                                        }
447                                });
448                                break;
449                        }
450                }
451
452        }
453*/
454
455        public String getName() {
456                return name;
457        }
458
459        public String getIconName() {
460                return iconName;
461        }
462
463        @Override
464        public void onUpgrade(Path path) {
465        }
466
467        @Override
468        public void onChange(Path path) {
469        }
470
471        @Override
472        public void onChildChange(Path path, ListChange.Action action) {
473        }
474
475        public final Frame getFrame() {
476                return frame;
477        }
478
479        public boolean changeValue(ValueControl component, Object newValue) {
480                log.debug("changing value of " + component + " to '" + newValue + "'");
481
482                if (localChanges == null) {
483                        localChanges = new HashMap<ValueControl, Object>();
484                }
485                localChanges.put(component, newValue);
486
487                return true;
488        }
489
490        public Path getInstancePath() {
491                assert frame.isActive();
492                return path;
493        }
494
495        public void pushLocalChanges() {
496                assert frame.isActive();
497                if (localChanges == null) {
498                        return;
499                }
500                final Map<ValueControl, Object> changes = localChanges;
501                localChanges = null;
502                endpoint.getEndpoint().invokeLater(new Runnable() {
503                        @Override
504                        public void run() {
505                                for (Map.Entry<ValueControl, Object> e : changes.entrySet()) {
506                                        final ValueControl key = e.getKey();
507                                        final Path p = path;
508                                        endpoint.getEndpoint().getInstance().storeValue(p, e.getKey().getParam(), e.getValue(), new StateFunctor() {
509                                                @Override
510                                                public void call(Exception e) {
511                                                        changes.remove(key);
512                                                        if (!changes.isEmpty()) {
513                                                                return;
514                                                        }
515                                                        log.debug("applied changes for: " + p);
516                                                        frame.invokeLater(new Runnable() {
517                                                                @Override
518                                                                public void run() {
519                                                                        fillPanelWithValues();
520                                                                }
521                                                        });
522                                                }
523                                        });
524                                }
525                        }
526                });
527        }
528
529}
Note: See TracBrowser for help on using the repository browser.